SUMMARY: How to configure a web service such that writing to the Event Log is always possible (regardless of caller)?
DETAILS:
I have a web service which writes an entry to the Application Log. I established the event source for this by means of a little console application and I think I understand that part of things. When I test this WS, I see I am successfully writing my entry to the Event log.
The virtual directory which hosts this WS does NOT allow anonymous access and is configured for Integrated Windows Auth only.
I have a web client application that calls this Webservice. When the web client site is configured for Integrated Windows Auth only, calls to the Webservice result in logging as desired.
Yet, if I change the web client site to allow anonymous access then the Webservice attempt to log results in an InvalidOperationException. I ignore it but it would be nice to know how to get logging in the webservice regardless of how it is called. Here is a bit of my code:
public FileService()
{
try
{
if (!EventLog.SourceExists(g_EventSource))
EventLog.CreateEventSource(g_EventSource, g_EventLog);
System.Security.Principal.WindowsIdentity UserIdentityInfo;
UserIdentityInfo = System.Security.Principal.WindowsIdentity.GetCurrent();
string AuthType = UserIdentityInfo.AuthenticationType;
if (AuthType == "Kerberos")
{ engineWSE.Credentials = System.Net.CredentialCache.DefaultCredentials; }
else
{ engineWSE.Credentials = new System.Net.NetworkCredential("u", "p", "domain"); }
EventLog.WriteEntry(g_EventSource,
"Caller: " + UserIdentityInfo.Name +
" AuthType: " + UserIdentityInfo.AuthenticationType,
EventLogEntryType.Information, 1);
}
catch (InvalidOperationException e)
{
// do nothing to ignore: "Cannot open log for source 'myAppSourceName'. You may not have write access."
}
}
The example in the constructor above is sort of contrived for here (I am mainly interested in being able to write out info related to errors in the web service).
I hope there is a way to configure the web service virtual directory (or the code within) so that logging is possible regardless of how it got called.
Network Service is allowed to write to the Event Log, but not create an event source. you could give permissions to HKLM\SYSTEM\CurrentControlSet\Services\Eventlog\ to allow it to create - but if you've already created it at install time, there's no need.
It's possible that it's failing on the SourceExists as well - since that requires enumerating the same registry key. I'd probably just remove the SourceExists/Create check and trust that it's there - if you're anonymous, you can't create it anyway.
You should also check your web.config.
If IIS is set to anonymous and web.config is set to windows / impersonate. Then it will be the anonymous IIS user that is trying to write to the event log.
Related
I'm currently working on a legacy asp.net webservice (asmx) hosted on an IIS 10 on Windows Server 2016.
In IIS the webservice is currently running with Identity ApplicationPoolIdentity. This can be changed.
I need to log Messages into the Windows Event Viewer, I use "Application Error" as the EventSource, as it apparently is an already existing event source on windows.
Therefore I refer to this post and use the following C# code:
try
{
System.Diagnostics.EventLog.WriteEntry("Application Error", "EventLog Test - Code EventLog", EventLogEntryType.Error);
}
catch (Exception ex)
{
// log error to file
}
When I run the application, I only get the following error log:
Unable to open log for source Application Error. You may not have write access.
Which settings could I adjust in IIS/registry/C# to achieve a log into my Windows Event Viewer?
Do you know how to solve this error?
This problem occurs because by default the user token of the application doesn't have the required user rights to write to the Windows event logs because of limited security access.
To provide the required permissions to the thread identity, modify the security of the event log through the below registry keys on the server machine. You should select the event log that your application is writing to:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Eventlog\Application\CustomSD
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Eventlog\System\CustomSD
The CustomSD registry value is of type REG_SZ and contains a security descriptor in Security Descriptor Definition Language (SDDL) syntax.
More information you can refer to this link: https://learn.microsoft.com/en-us/troubleshoot/developer/webapps/aspnet/development/fail-write-event-log#resolution.
We have a Windows Service that checks a configuration file, and based on those settings, starts a new Process (let's call this ClusterProcess) which connects to a database and does the actual functionality.
There is a test project, that cuts out the Windows Service's part in the play, and just provides ClusterProcess with the necessary settings.
For our current test environment, we have the database on a cloud and I would say, rather restricted permissions.
My problem is this: when I run the test project, ClusterProcess gets its settings and things happen on the database. However, when I run the Windows Service, nothing gets changed on the database.
I've been using log4net to try and see where things fall over, but it seems to fail silently.
So the Windows Service calls this:
_ClusterRunnner.StartAllClusters();
In this method mentioned above, this is (amongst other things) what happens, and I get both log statements:
_ClusterProcesses = new List<Process>();
log.Debug("Cluster Name: " + cluster.Name + " ConfigPath: " + _ConfigPath);
_ClusterProcesses.Add(Process.Start(startInfo));
log.Debug("After process spawn.");
Okay, so the Process that it should start is ClusterProcess.exe, and in the Main method of that project I don't get any of the log statements that should be given, for instance:
log.Debug("Going to Start Processing");
_ClusterProcessor = new ClusterProcessor(config);
_ClusterProcessor.StartProcessing();
log.Debug("Finished Processing");
With log4net I have a special logger for ClusterProcess.exe (two appenders, a RollingFileAppender and an AdoNetAppender) and similar appenders for the Windows Service. (The Windows Service process holds a lock to its RollingFileAppender, so I had to create a special one for ClusterProcess.exe. The AdoNetAppenders are for nicely formatted logs after the connection to the database has been made.)
But now, the test project just calls this directly:
ClusterProcessor x = new ClusterProcessor(config);
x.StartProcessing();
and it works! I get the log statements in StartProcessing() and the database is changed.
So any ideas?
From my experience with services they run with very limited rights.
So if your Test(possible windows application) runs perfectly then I guess it has something to do with user rights.
I guess you can't realy access the filesystem so if your logger writes to a place on the harddrive you should think about the windows event log --> Write to Windows Application Event Log without registering an Event Source
To try if it has something to do with user rights just open your service in the windows service settings and let it log on as your administrator account and see if it has something to do with this.
I'm moving the deployment of a web app from an Azure Website into a Web Role in a Cloud Service.
Part of the migration has involved reserving some local storage in the role config and changing interactions with the local file-system to use the following mantra to find a path that is good for writing to:
LocalResource tempStorageResource = RoleEnvironment
.GetLocalResource("SomeRoleStorage");
var targetFolderPath = tempStorageResource.RootPath;
However, I'd like to keep things working in the WebSite instance. I'm going to write a path provider that abstracts the actual location away. Part of implementing this will require detecting whether I'm running locally/in the debugger, but I also need to know whether the running code is running under a WebSite or a WebRole. How can I do this?
public class AzurePathProvider : ILocalStoragePathProvider
{
public string GetStoragePath(string key)
{
var isWebRole = //????;
if(isWebRole)
{
LocalResource tempStorageResource =
RoleEnvironment
.GetLocalResource(key);
return tempStorageResource.RootPath;
}
else
{
return "/some/other/storage/location";
}
}
}
Check for RoleEnvironment.IsAvailable to decide if the code is running in Cloud Service or not. It will always be true when your code is running in Cloud Service otherwise it will be false.
Furthermore to detect if the code is running in compute emulator, you can check for RoleEnvironment.IsEmulated along with RoleEnvironment.IsAvailable.
I'm calling the following code from Windows service that was written with C#:
try
{
ServerManager m = new ServerManager();
if(m != null)
{
SiteCollection sites = m.Sites; //I get exception here
}
}
catch (Exception ex)
{
}
I get this exception:
{"Filename: redirection.config\r\nError: Cannot read configuration
file\r\n\r\n":null}
What does that mean? And is there any way to predict it in ServerManager or my m variable before it's thrown?
Update: After looking at your comment now I can answer the question fully, the problem is your application is referencing the wrong Microsoft.Web.Administration.dll, seeing the error tells me you are referencing the IIS Express version and not the "full" IIS Version (7.0.0.0). So please modify your application code to add a reference to the one that is in c:\windows\system32\inetsrv\Microsoft.Web.Administration.dll instead.
This is a permissions problem.
You will need to make sure to run the Windows Service as an identity that is either a member of the Administrators group or SYSTEM. My guess is you might be running the service as Local Serivce or Network Service and those do not have permission to read the configuration files that sit in %windows%\system32\inetsrv\config.
Other Info:
Redirection.config is a file that IIS uses to determine if it should read its configuration from the normal path (%windir%\system32\inetsrv\config\applicationHost.config) or should read it from say an external UNC file share when centralized configuration is used for many servers. That is why this is one of the first files to be parsed and hence you get that access denied error for that specific file.
Regarding the predicting, the best thing to do would be to create it within a try/catch and handle that. There are many exceptions that could happen when reading configuration, such as permissions (you could predict this one by making sure you can read (say File.OpenText()) to Redirection.config, ApplicationHost.config in %windir%\system32\inetsrv\config but that is guessing and there are others such as access to encryption keys for passwords, invalid config, etc, etc.)
I'm having trouble figuring out why my web service is not working correctly when called from my asp.net application in production only.
I am able to run my asp.net application locally, calling the web service (in production) and it completes correctly.
I am able to modify the web.config to allow me to use the test form on the production web service and it calls correctly.
I'm calling the web service in a shared dll, and I already check that I am actually getting the updated dll.
This is a very basic web service to log any exceptions on our sites that aren't handled elsewhere. I'm adding additional parameters to it, and also moving it to another project so it is grouped with more of our web services. It is in it's own asmx file called ExceptionServices
My shared dll has a class ErrorHandler which calls (at this point, a test method) TestEmail(string to) and all that does is sends me an email.
Like I said, when running my app locally, it calls the production web service and all is good.
I am running in a hosted environment and am unable to install the remote debugging tools so I cannot step through the production code (unless anybody knows any tricks).
It just seems like this should work (banging head on keyboard)...
Here is my basic web method:
[WebMethod]
public void TestEmail(string to)
{
MailMessage mm = new MailMessage("no-reply#mydomain.com", to, "test", "body here");
SmtpClient client = new SmtpClient("localhost"); // already tried tweaking smtp server, and all my options work when I use the test form
client.Send(mm);
}
ErrorHandler class in the shared dll
public class ErrorHandler
{
public static void ThrowError(Exception ex, string sitename, string ip, string username)
{
//if (ip != "127.0.0.1") // exclude local errors when developing
{
EILib.ExceptionServices.EIExceptionHandler eh = new EILib.ExceptionServices.EIExceptionHandler();
eh.TestEmail("senloe#....com");
}
}
}
and finally my global.asax where it all begins:
protected void Application_Error(object sender, EventArgs e)
{
//Get the Error.
System.Exception anError = Server.GetLastError();
EILib.ErrorHandler.ThrowError(anError, "mydomain.com", EILib.Utilities.GetUserHostAddress(Request), User.Identity.Name);
....
}
Verify that the SMTP service is running on your production server. Check that it is configured to allow the default credentials (a hosting company might well require credentials for email). Check that the service is configured to receive mail from from the local computer (as opposed to using one of the pickup directory delivery methods).