How do I remotely access self-hosted Nancy service? - c#

I am creating a simple Windows service that hosts a Nancy instance to provide views of its internal data. Everything works as expected when using a browser on the local machine; I see the view that it serves up. However, I cannot find any reason why it will not access from a remote browser (on the same network). Access from a remote browser simply delays a while; IE will eventually display "This page can’t be displayed;" Safari on an iPad shows the partial progress bar for a while and does nothing.
I'm binding using all local IPs, not just localhost.
I am using the GetUriParams() function at this link to discover all local IP addresses for binding. http://www.codeproject.com/Articles/694907/Embed-a-web-server-in-a-windows-service
_nancyHost = new NancyHost(GetUriParams(port));
_nancyHost.Start();
I discovered at this page that binding to localhost works for local access only. http://forums.asp.net/t/1881253.aspx?More+SelfHost+Documentation
The IPs that this function discovers are for Ethernet adapter, Wireless adapter, and two VMware Network adapters from a prior installation of a VMware player. I've tried the remote access both by machine name and by literal IP to the Ethernet adapter.
I added entries to urlacl list.
I have used the netsh http add urlacl command as recommended in many places, including at this link: Remote access to a Nancy Self Host
If I perform netsh http show urlacl, I see the entry for the port I'm using.
I tried different Nancy configs
If I set the Nancy configuration option for UrlReservations.CreateAutomatically, I will get security prompts, which after allowing, I see new entries in netsh http show urlacl list output for all of the local IPs, but it still does not allow remote access. I also tried the RewriteLocalHost option true and false.
I've tried starting Nancy with http://+:3684 or http://*:3684 (which gets parsing exception from Uri()) and with http://0.0.0.0:3684 (which gets exception from AddAllPrefixes() within HttpListener()).
I added the EXE to Windows firewall
I have created firewall exceptions as described here: https://msdn.microsoft.com/en-us/library/ms733768.aspx
The associated rule shows Private,Public and "Any" for every column with both TCP and UDP.
I tried running Nancy in different environments. I've run the code in: the Windows Service running as Local System, a console app within Visual Studio 2013 debugger, and the console app Run As Administrator.
I imagine it's a simple security setting, but I've googled and searched and tried various things for a couple of days now.
What am I missing?

This answer provided the clue I needed.
https://stackoverflow.com/a/21364604/1139376
This is because HttpListener is built on top of http.sys which will listen on the port you specified on behalf of your program.
It wasn't my EXE doing the actual listening. All I needed to do was to add an Incoming rule to the Windows Firewall set for the "System" program and the specific TCP port I'm using. That allowed remote access.

Use the HostConfiguration and let Nancy make the URL reservations automaticaly for you.
var config = new HostConfiguration
{
RewriteLocalhost = true,
UrlReservations = new UrlReservations { CreateAutomatically = true }
};
host = new NancyHost(new Uri("http://localhost:8080"), new DefaultNancyBootstrapper(), config);
host.Start();
Note that this will force ACL to create network rules for new ports if they do not already exist.

Related

Authenticating RPC Server with NTLM

I am currently trying to improve an RPC Server I'm responsible for, both server and client run on the same machine locally, however I would like to restrict the server so that it only allows administrator (including built in /LocalSystem account) to connect to the rpc server through a named pipe.
First of all I am using the following library as a wrapper for the RPCserverApi/RPCClientApi:
https://github.com/csharptest/CSharpTest.Net.RpcLibrary
I create the Server like so:
server = new RpcServerApi(IId, MaxCalls, ushort.MaxValue, true);
server.AddProtocol(RpcProtseq.ncacn_np, Id, MaxCalls);
// Set authentication
server.AddAuthentication(RpcAuthentication.RPC_C_AUTHN_WINNT);
However when I check the named pipes security it still shows like it's not restricted at all, and my client can still connect even though I have yet to change that to specify authentication.
In addition I can check the access to that named pipe and I get:
\\.\pipe\myNamedPipe
RW Everyone
RW NT AUTHORITY\ANONYMOUS LOGON
RW BUILTIN\Administrators
Okay, So for anyone else that ran into this problem There's a few things I needed to do which was not exposed in the library I was using. So instead I created my own wrapper.
When Registering the Rpc Interface with RpcServerRegisterIf2() I had to pass through the flag:
RPC_IF_ALLOW_SECURE_ONLY
Then In addition when setting up the protocols for the RpcServer: RpcServerUseProtseqEp() I also had to pass through an SDDL, to describe the restrictions on the end point. You can find a description of SDDL's here:
https://learn.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-definition-language
To do this I created an Ace String, then used ConvertStringSecurityDescriptorToSecurityDescriptor() to create the correct object. This then locked down the end point like:
\\.\pipe\myNamedPipe
RW BUILTIN\Administrators
But also it enforced on the server that only authenticated accounts could reach it
My issue originally reported was full of misunderstandings about RPC Servers and Named pipes, I thoroughly recommend reading and understanding the following articles, as they were very helpful to me.
https://csandker.io/2021/01/10/Offensive-Windows-IPC-1-NamedPipes.html
https://csandker.io/2021/02/21/Offensive-Windows-IPC-2-RPC.html

Port requirements for System.DirectoryServices.ActiveDirectory

We have a product which implements role based authentication using AD.
At the start of this application, it tries to enumerate through all the domains in the current forest to fetch some information which will be used later.
A snippet of the code is shown below
Domain currentDomain = Domain.GetDomain(new DirectoryContext(DirectoryContextType.Domain));
Forest currentForest = currentDomain.Forest;
string forestName = currentForest.Name;
foreach (Domain domain in currentForest.Domains)
{
//processing code.
}
The above code runs fine until 3rd statement and the forestName variable is initialized properly with the current forest name.
But it fails in the foreach loop when it tries to execute currentForest.Domains
We are getting an ActiveDirectoryServerDownException with the error message "The specified domain either does not exist or could not be contacted" with an error code 1355.
From the below link, I got to know that this could be a dns misconfiguration or the ports might be blocked by firewall.
https://social.technet.microsoft.com/Forums/msonline/en-US/53804e9d-ccdd-450a-967b-b7e8f67cddae/active-directory-error-code-1355?forum=winserverDS
I am trying to understand the ports that need to be open for communication on server machine(Active directory server) and client machine.
The below link specifies so many number of ports which confused me.
https://support.microsoft.com/en-us/help/832017/service-overview-and-network-port-requirements-for-windows
Can some one provide me information on what are the ports that are required to be open on server and client machines so that I can successfully enumerate all the domains in the forest.
Those methods use the LDAP protocol to talk to AD, which is all over port 389.
Just to be sure, I ran your code and watched the network connections it used, and it only used port 389.
As a side note, you can simplify your code a little by using Forest.GetCurrentForest() instead of looking up the current domain then looking up the forest for that domain.
Forest currentForest = Forest.GetCurrentForest();
Either way will get the same job done.

.Net's Directory Services throws a strange exception

I have a small C# solution used to check users credentials. It works fine for two of my teammates, but on my PC I get an exception.
The relevant code:
PrincipalContext context = new PrincipalContext(ContextType.Domain);
if (context.ValidateCredentials(System.Environment.UserDomainName + "\\" + usr, pwd))
return true;
else
return false;
And the exception is:
DirectoryOperationException, "The server cannot handle directory requests.".
I tried creating context with the explicit server name and the 636 port number, but this didn't help as well.
Any ideas?
I had this problem too using IIS Express and VS 2010. What fixed it for me was a comment on another thread.
Validate a username and password against Active Directory?
but i'll save you the click and search... :) Just add ContextOpations.Negotiate to you Validate Credentials call like below.
bool valid = context.ValidateCredentials(user, pass, ***ContextOptions.Negotiate***);
I had this issue: things were working on my dev machine but didn't work on the server. Turned out that IIS on the server was set up to run as LocalMachine. I changed it to NetworkService (the default) and things started working.
So basically check the user of the app pool if this is running on IIS.
I had to just create a new app pool and assign it .NET 2.0, then assign the new app pool to our web app, and it started working. We had .NET 3.5 SP2, so the hotfix wasn't ideal for us. Since the WWW service is usually Local System, I questioned that too. But since it was .NET and security related, I gave a shot at the app pool first and it worked.
Perhaps you need the hotfix?
FIX: DirectoryOperationException exception
And you are an Admin or the id that your service is running under is an Admin on your PC right?
I take it you already looked into this:
System.DirectoryServices.Protocols
"You may receive a less than helpful DirectoryOperationException(“The server cannot handle directory requests.”) what isn’t quite so amusing about this is that it didn’t even try to communicate with the server. The solution was to add the port number to the server. So instead of passing “Server” to open the LdapConnection, I passed “server:636”. By the way, LDAPS is port 636 – rather than the 389 port used by LDAP."
Good point, I wouldn't expect that Win7/.NET 3.5 would need that patch. How about the info provided in this question:
Setting user's password via System.DirectoryServices.Protocols in AD 2008 R2

How do I properly host a WCF Data Service that connects to SQLServer in IIS? Why am I getting errors?

I'm playing around with WCF Data Services (ADO.NET Data Services). I have an entity framework model pointed at the AdventureWorks database.
When I debug my svc file from within Visual Studio, it works great. I can say /awservice.svc/Customers and get back the ATOM feed I expect.
If I publish the service (hosted in an ASP.NET web application) to IIS7, the same query string returns a 500 fault. The root svc page itself works as expected and successfully returns ATOM. The /Customers path fails.
Here is what my grants look like in the svc file:
public class AWService : DataService<AWEntities>
{
public static void InitializeService( DataServiceConfiguration config )
{
config.SetEntitySetAccessRule( "*", EntitySetRights.All );
config.SetServiceOperationAccessRule( "*", ServiceOperationRights.All );
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
}
Update: I enabled verbose errors and get the following in the XML message:
<innererror>
<message>The underlying provider failed on Open.</message>
<type>System.Data.EntityException</type>
<stacktrace>
at System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(
...
...
<internalexception>
<message>
Login failed for user 'IIS APPPOOL\DefaultAppPool'.
</message>
<type>System.Data.SqlClient.SqlException</type>
<stacktrace>
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, ...
It looks to me like this is a SQL authentication error, IIS is running its appPool under a user that does not have access to your SQL server, when you ruin in Visual Studio (locally) it will be a different user. Check the user that the IIS on the server is using and make sure it has rights to do what you want in SQL.
Try to change the connection string attribute Integrated security to False
Quick solution with IIS Express
Create a firewall exception to allow HTTP requests through the firewall on the port that IIS Express is using.
Get the IP address of the development computer, if necessary, by running ipconfig.
Find the IIS Express configuration file, applicationhost.config, in the folder %USERPROFILE%\Documents\IISExpress\config. The USERPROFILE environment variable typically has a value of C:\Users\.
Open applicationhost.config with Notepad or another text editor and make the following changes.
Find the site element for the web service, WebServiceForTesting.
If you don’t see the site element for the web service, you have to deploy the service at least one time to create the element.
Within the bindings section of the site element, copy the binding element and paste a copy directly below the existing binding element to create a second binding.
In the new binding element, replace localhost with the computer’s IP address.
Save the changes.
Run Visual Studio as administrator and open the Visual Studio solution.
In the phone app project, remove the service reference to the service if you have previously added it.
Add a new service reference to the reconfigured web service. In the Add Service Reference dialog box, in the Address box, replace localhost with the IP address of your development computer. Click Go.
The second binding for the service in the WCF project is discovered and displayed. Click OK.
A new service reference that uses the IP address of the development computer is added to the Windows Phone project.

Can a WebServiceHost be changed to avoid the use of HttpListener?

I am looking for a way to use a WCF WebServiceHost without having to rely on the HttpListener class and it's associated permission problems (see this question for details).
I'm working on a application which communicates locally with another (third-party) application via their REST API.
At the moment we are using WCF as an embedded HTTP server. We create a WebServiceHost as follows:
String hostPath = "http://localhost:" + portNo;
WebServiceHost host = new WebServiceHost(typeof(IntegrationService), new Uri(hostPath));
// create a webhttpbinding for rest/pox and enable cookie support for session management
WebHttpBinding webHttpBinding = new WebHttpBinding();
webHttpBinding.AllowCookies = true;
ServiceEndpoint ep = host.AddServiceEndpoint(typeof(IIntegrationService), webHttpBinding, "");
host.Open()
ChannelFactory<IIntegrationService> cf = new ChannelFactory<IIntegrationService>(webHttpBinding, hostPath);
IIntegrationService channel = cf.CreateChannel();
Everything works nicely as long as our application is run as administrator. If we run our application on a machine without administrative privileges the host.Open() will throw an HttpListenerException with ErrorCode == 5 (ERROR_ACCESS_DENIED).
We can get around the problem by running httpcfg.exe from the command line but this is a one-click desktop application and that's not really as long term solution for us.
We could ditch WCF and write our own HTTP server but I'd like to avoid that if possible.
What's the easiest way to replace HttpListener with a standard TCP socket while still using all of the remaining HTTP scaffolding that WCF provides?
Your problem is not related to HttpListener.
Your problem is:
* You have a oneClick application with limited permissions that
* Tries to open a Server port.
This is a contradiction. An untrusted limited permission application should NOT OPEN A SERVER PORT. This is why this is not allowed per definition.
Have you tried opening a normal socket port? It should not work either.
In general, limited trust end user applications should not host a web service ;)
That said, I ahve been in a similar situation trying to use WCF in a driver communication scenario - thank heaven my application runs with full permission.
You can easily compose your own stack via CustomBinding, using the higher level protocol stuff "as is", and rolling your own version of HttpTransport that isn't backed by HttpListener or IIS. Do-able, sure, but it's a lot of work. Take the existing HttpTransport bits apart with Reflector- there are a LOT of moving parts in there. You could probably hack up a simple PoC over Socket in a day or two if you don't need anything fancy like HTTPS or chunking, but making it robust will be tricky. Here's a good wrapup of a bunch of resources (may be a bit dated now).
You could also look at ripping apart enough of Cassini to make it hostable in your app, and loading the WCF pipeline in there (via .svc files and the service activation handler)- it'd require writing very little new code that way, but still give you a fairly robust and tested webserver.

Categories