Showing posts with label IIS. Show all posts
Showing posts with label IIS. Show all posts

Tuesday, November 3, 2015

Login failed for user xxx on IIS 7

Recently I migrated one of my ASP.NET MVC projects to another machine.  I updated the Entity Framework .edmx file, modified the connection string, added a specific application pool PM in IIS, and added my application to use this application pool.  After I ran it, I got the exception "Login failed for user IIS APPPOOL\PM".

After some digging, I found some useful links, such as this and this.  I list all necessary steps to run an ASP.NET MVC application in IIS 7 here in case somebody needs helps.

1. In IIS, I like to create a dedicated application pool MyPool, and specify the correct .NET framework version and Managed Pipeline Mode, mostly Integrated.

2. Check the created pool, and we can see by default the Identity using ApplicationPoolIdentity.  This is the recommended way to use in a development environment.  Some blogs suggest changing this to "Local System" or others.  If this is changed to "Local System", then step 4 is not needed.  However, this change may bring some security risks.  You can check the links I listed and see some good arguments.  I will keep ApplicationPoolIdentity for my application.

3. In IIS, under Default Web Site, create an application for the website, give a proper Alias and point to the physical path of the project.  In a development environment, I just directly point to the web site project. Then let the application use the dedicated pool.

4. In SQL Studio, add new login IIS APPPOOL\MyPool.  You may check this link to see some good discussions.  Remember to assign proper roles to this login.  We don't want to give too much privilege for this login.

5. Sometimes we may need to enable MSDTC on the machine to support distributed transaction if the application uses distributed transaction.   On Win7, go to Control Panel -> Administrative tools -> Component Services, expand Computers -> My Computer -> Distributed Transaction Coordinator -> Local DTC, and right click Properties.  Under Security tab, check Network DTC Access, Allow Remote Clients, Allow Remote Administration, Allow inbound, Allow outbound.  Choose Manual Authentication Required.  Click OK.  A reboot may be needed here.

Wednesday, October 8, 2008

WCF Service: support transaction and IIS in an intranet environment

Currently I am working on a web project whose data access layer is separated as a standalone WCF service. Both the web server and WCF service are in the same intranet environment and we use Windows integrated authentication to verify users in a domain, so the security issue is not that critical. An additional requirement is that WCF service needs to know the client credential which logs in the web application and store that information into database for later audit. So the web server should pass over the client credential to the WCF service.

I use IIS to host my WCF service and web application. The WCF service and web application could be on different computers. In IIS I set my web application to use Windows integrated authentication, so users have to provide their domain user name and password to access the web application.

basicHttpBinding with “TransportOnly” security setting

In this situation, I simply use basicHttpBinding with the security setting “TransportOnly” and it works well. Here is the relative server configuration code:
<basicHttpBinding>
<binding name="basicBinding">
<security mode = "TransportCredentialOnly">
<transport clientCredentialType="Windows"/>
</security>
</binding>
</basicHttpBinding>

I use the following code to get the user name in the WCF service:
string user = System.ServiceModel.OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name;

The client configuration code is the following:
<basicHttpBinding>
<binding name="BasicHttpBinding_IMyService" >
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" realm="" />
</security>
</binding>
</basicHttpBinding>

The code works well getting the user name in a Windows domain from a client.

Drawbacks of basicHttpBinding

Despite the obvious disadvantage in security, we still can make do with basicHttpBinding since all our applications are in an internal Windows domain. Another fatal weakness, however, forced me to transfer from basicHttpBinding to wsHttpBinding. basicHttpBinding doesn’t support Transaction! I have a business mode that I need to call different WCF functions several times and all the calls should be within one transaction.

In .NET framework, we usually use TranscationScope to automatically manage transactions, especially in a distributed environment. WCF supports transaction, and provides many relative classes and attributes. However, all of them are not working with basicHttpBinding.

wsHttpBinding with transaction support

wsHttpBinding can have different security setting. There are many good blogs we can find in the internet. If we use Transport security with wsHttpBinding, we actually use SSL/TLS over HTTP, that is, HTTPS. We cannot use HTTP prefix in this situation. Transport is good for point-to-point scenarios, but in end-to-end scenarios where we have intermediaries existing, we should go for Message security.

The following is what I changed to my basicHttpBinding source code.

1. Install a certificate in WCF for transport security to support HTTPS. Transport security needs SSL, so we have to install a server certificate first.

2. In server side, the configuration is like this:
<wsHttpBinding>
<binding name="wsBinding" transactionFlow="true" >
<security mode = "Transport">
<transport clientCredentialType="Windows"/>
</security>
</binding>
</wsHttpBinding>

<serviceBehaviors>
<behavior name="DBService.Service1Behavior">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true"/>
<serviceCredentials>
<serviceCertificate storeName="My" findValue="CN=localhost"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>

3. In WCF service source code, add attributes in several places:

In the service interface:
[ServiceContract()]
public interface IMyService
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
bool UpdateData(AInfo info);
}

In the implementation class:
[ServiceBehavior(TransactionAutoCompleteOnSessionClose = true,
ReleaseServiceInstanceOnTransactionComplete = true,
TransactionIsolationLevel = System.Transactions.IsolationLevel.Serializable,
TransactionTimeout = "01:00:00",
InstanceContextMode = InstanceContextMode.PerSession)]
public class MyService : IMyService
{
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public int UpdateData(AInfo info)
{
return 1;
}
}

4. The client configuration code is like this:
<wsHttpBinding>
<binding name="WSHttpBinding_IAssayService" closeTimeout="00:01:00"
openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"
bypassProxyOnLocal="false" transactionFlow="true" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="1000000"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<security mode="Transport">
<transport clientCredentialType="Windows" realm="" />
<message clientCredentialType="UserName" negotiateServiceCredential="false"
algorithmSuite="Default" />
</security>
</binding>
</wsHttpBinding>

5. The client source code looks like this:
TransactionOptions options = new TransactionOptions();
options.Timeout = new TimeSpan(1, 0, 0);
MyServiceClient client = new MyServiceClient();
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, options))
{
client. UpdateData (source1);
client. UpdateData (source2);
client. UpdateData (source3);
}

Now I can call my service function any times. All the operations will be rolled back if any of them fails.