Accessing WCF application through Service Fabric reverse proxy - c#

We've made a a WCF application that we're hosting inside an On-Premise Service fabric cluster. Accessing it through the Service Fabric reverse proxy is giving us some difficulties.
Our cluster has 3 nodes(eg. 10.0.0.1-3) and the application should be accessible through the reverse proxy (listening on port 19081) on every node. Unfortunatly it only works through the SF reverse proxy on the node hosting the WCF application(also listening on port 19081). Accessing it through the other nodes results in a 400 bad request.
If we run the WCF service on a different port, we can access it directly / locally, but not through the Service Fabric Reverse Proxy.
We're running multiple ASP.NET Core/REST services on the cluster and these work fine.
Example
If the service is running on the 10.0.0.1 node we can access it through:
http://10.0.0.1:19081/myserviceType/soaphost/myservice.svc
However these URL's result in a 400 bad request status code:
http://10.0.0.2:19081/myserviceType/soaphost/myservice.svc
http://10.0.0.3:19081/myserviceType/soaphost/myservice.svc
Code example
We're using the following code to create the WCF Service Instance Listener:
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new ServiceInstanceListener[] {
CreateWcfListener("ServiceEndpoint", serviceProvider.GetService<ServiceType>())
};
}
private ServiceInstanceListener CreateWcfListener<T>(string endpoint, T serviceImplementation)
{
return new ServiceInstanceListener((context) =>
{
var endpointConfig = context.CodePackageActivationContext.GetEndpoint(endpoint);
int port = endpointConfig.Port;
string scheme = endpointConfig.Protocol.ToString();
string host = context.NodeContext.IPAddressOrFQDN;
string uriStem = endpointConfig.PathSuffix;
string uri = $"{scheme}://{host}:19081{context.ServiceName.AbsolutePath}/{uriStem}";
CustomBinding listenerBinding = CreateListenerBinding();
WcfCommunicationListener<T> listener = new WcfCommunicationListener<T>(
wcfServiceObject: serviceImplementation,
serviceContext: context,
address: new EndpointAddress(uri),
listenerBinding: listenerBinding);
return listener;
}, endpoint);
}
We would like to know why it doesn't work, but more importantly how to fix it.

Related

Set Custom Host Name as part of changing the Endpoint Address of a Soap Client

I am presently in a scenario where I need to have multiple servers that live behind a Load Balancer talk to each other directly, and I need to communicate with specific servers for PUSH notifications. This is for a chat tool that requires users that have been moved to different servers by a load balancer to still be able to talk to one another live.
The actual pushes are being handled with Signal-R, and I have all of that working. So here is the actual complication:
Normally, this would be simple enough to do by targeting them via IP Address to bypass the load balancer. However, this is complicated because the servers expects a specific Host name or it will reject the request.
I know it is possible to do this with a WebRequest, but would like to avoid having to build a proxy if I can help it.
Here is the piece I have where I'm trying to send a global push to tell everyone across all connected servers to update their buddy lists because someone logged in or out.
private void NotifyUsersChangedGlobal()
{
List<string> addressesToNotify = ChatUsers.Select(x => x.ServerIP).Distinct().ToList();
foreach (string address in addressesToNotify)
{
ChatUplinkSoapClient client = BuildClient(address);
client.NotifyUsersChanged();
}
}
And this is the Client Builder where (I assume) I need to handle assigning the custom Host name to ride on top of the IP Address
private ChatUplinkSoapClient BuildClient(string endpointIP)
{
string relativeUrl = "/WebServices/ChatUplink.asmx";
//Turn on HTTPS
HttpBindingBase binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
//Link together the IP Address and the asmx route
EndpointAddress endpoint = new EndpointAddress(endpointIP + relativeUrl);
//Make the Client
ChatUplinkSoapClient client = new ChatUplinkSoapClient(binding, endpoint);
//Need to set Host header to "HostHeaderName", or the server will reject the request.
return client;
}
Ideas?

Connect to signalR hub from c# web api

I've been tasked with trying to move our signalR hub to an azure cloud service with a service bus backplane. No problems there. The javascript client is able to get the hubs.js and connect without errors. We also have a web api project that needs to send messages to the hub but I cannot get it to connect. Everything I've tried doesn't work and the connection times out. I must be missing something but, since this is my first time working with signalR and Azure, I don't know what it is. The web api is hosted on IIS.
Here is the code I am trying to use to connect:
private async void InitializeHub()
{
string connectionString = "http://xxxx-xxxxx.cloudapp.net/signalr";
var hubConnection = new HubConnection(connectionString, useDefaultUrl: false);
//var hubConnection = new HubConnection(connectionString);
HubProxy = hubConnection.CreateHubProxy("clientPortalHub");
await hubConnection.Start();
}
Is there some configuration I am missing? Anything need to be done in IIS? I'm not getting any helpful error messages, just that the connection times out. I can hit the url (the real one, not the one I pasted) in a browser and get "Unknown transport".
If it helps here is the startup from the hub:
public void Configuration(IAppBuilder app)
{
// Any connection or hub wire up and configuration should go here
string connectionString = "<omitted>";
GlobalHost.DependencyResolver.UseServiceBus(connectionString, "clientPortalHub");
app.Map("/signalr", map =>
{
map. UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
// You can enable JSONP by uncommenting line below.
// JSONP requests are insecure but some older browsers (and some
// versions of IE) require JSONP to work cross domain
// EnableJSONP = true
};
hubConfiguration.EnableDetailedErrors = true;
map.RunSignalR(hubConfiguration);
});
}

Windows phone 8 consuming a WCF service operation contract error

I have a WCF service hosted by in a website I developed. The site is hosted by a hosting company. I also have a windows phone 8 application that consumes the service. My problem is everytime I query an operation contract with a parameter I get an error "System.ServiceModel.COmmunicationException: The remote server returned error: NotFound" but if I query an operation that takes no parameter the service works.
[ServiceContract]
public interface IParcelService
{
[OperationContract]
string TrackParcel(string pNumber);
}
public class ParcelService:IParcelService
{
public string TrackParcel(string pNumber)
{
return "some string";
}
}
The above a snippet from the service then on the client i have a reference to the service and I call it a follows
var service = new ParcelClient();
service.TrackParcel("1234");
service.TrackParcelCompleted +=(s,args) =>{var res = args.Result;//At this point reslt throws an exception};

C#: method to add WCF authentication, username + SSL?

I've written both a WCF client and a remote internet WCF server.
The remote WCF server is running WPF hosted in a traditional Windows Service wrapper (i.e. not IIS).
Currently, its working perfectly with basic HTTP binding. I'm using Visual Studio 2010 + .NET 4.0 + C#.
Can anyone point me in the direction of the right steps to alter my code so that I can add username + SSL authentication?
EDIT:
At the service end, I've overridden UserNamePasswordValidator as follows:
public class CustomUserNameValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
Console.WriteLine("Got to here");
}
}
At the service end, I've specified a link to the username validation class:
ServiceHost serviceHost = new ServiceHost(typeof(PhiFeedServer.PhiFeed)); // ,baseAddress);
const bool passswordAuthentication = true;
if (passswordAuthentication)
{
// These two lines switch on username/password authentication (see custom class "CustomUserNameValidator" in common file PhiFeed.svc.cs)
// See http://msdn.microsoft.com/en-us/library/aa354513.aspx
serviceHost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
serviceHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUserNameValidator();
}
// Start the service
serviceHost.Open();
At the client end:
EndpointAddress endpointAddress = new EndpointAddress("http://localhost:20000/PhiFeed?wdsl");
BasicHttpBinding serviceBinding = new BasicHttpBinding();
serviceBinding.ReceiveTimeout = new TimeSpan(0, 0, 120);
proxy = new PhiFeedClient(serviceBinding, endpointAddress);
proxy.ClientCredentials.UserName.UserName = "myusername";
proxy.ClientCredentials.UserName.Password = "mypassword";
However, when I run everything, it never even calls the username validator - whats going on?
If i am getting this right, you will need to play around with service behaviour. I did that in 3.5 sp1 it should be the same in 4.0 i think.
read this:
http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/7d589542-277a-404e-ab46-222794422233/
Aha! Found the solution to my problem.
Microsoft provides example code which demonstrates how to add username/password + SSL authentication to a console app.
Search for "Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4", download, unzip into C:, then run the sample here:
C:\WF_WCF_Samples\WCF\Extensibility\Security\UserNamePasswordValidator\CS

How do I set up a secure WCF service behind a firewall?

I have a WCF service that is behind an enterprise-class firewall, which is doing both hostname and port translation, e.g.:
https://ws.address.com/Service.svc --> https://serv.internal.com:44000/Service.svc
The service is secured with SSL-128 and requires a client certificate.
Because the internal server name is not accessible from outside the firewall, we had to implement a ServiceHostFactory to translate the WSDL and XSD import references that WCF generates:
public class MyCustomFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(
Type serviceType, Uri[] baseAddresses)
{
MyCustomHost customServiceHost =
new MyCustomHost(serviceType, baseAddresses);
return customServiceHost;
}
class MyCustomHost : ServiceHost
{
public MyCustomHost(Type serviceType,
params Uri[] baseAddresses)
: base(serviceType,
GetBaseAddresses(serviceType, baseAddresses))
{
}
protected override void ApplyConfiguration()
{
base.ApplyConfiguration();
}
private static Uri[] GetBaseAddresses(
Type serviceType, params Uri[] baseAddresses)
{
UriBuilder newBaseAddress = new UriBuilder();
newBaseAddress.Path = "/" + serviceType.ToString() +
".svc";
// from config
newBaseAddress.Host =
MyCustomSettings.ServiceBaseAddress;
if (baseAddresses.Length > 0)
{
newBaseAddress.Scheme = baseAddresses[0].Scheme;
}
return new Uri[] { newBaseAddress.Uri };
}
}
}
Here's the problem with this: unless the service is hosted on the internal machine on the default SSL port of 443, we get the error:
No protocol binding matches the given address 'https://ws.address.com/Service.svc'. Protocol bindings are configured at the Site level in IIS or WAS configuration.
It appears, from tinkering, that if we change the internal server to host the service on 443, or configure the firewall to forward from 44000 to 44000, everything works. Those aren't options in our production environment, though.
Edit: Forgot to mention, we tried to use an IWsdlExportExtension to flatten the WSDL, but that caused severe problems with the proxy code generation in svcutil or VS2008, so we scrapped the idea.
Does anyone know any way around this? I'm pulling my hair out!
Thanks in advance!
You may need to explicitly create your own Binding (i.e., ServiceModel.WSHttpBinding) and add a Service Endpoint (.AddServiceEndpoint(..) ) with that binding.
http://msdn.microsoft.com/en-us/library/system.servicemodel.servicehost.addserviceendpoint(VS.85).aspx
Have you tried putting the ip port in the address (from the question it did not look like it was used everytime):
https://ws.address.com:44000/Service.svc
Another thing that it may be is, is WCF listening for https traffic on that port, see:
http://msdn.microsoft.com/en-us/library/ms733768.aspx
You shouldn't change the base addresses in the factory. Write an extension instead to modify the WSDL. This would be a "IWsdlExportExtension" and you wanna overwrite ExportEndpoint to modify the endpoint addresses. This will leave your service listening to the correct base address.
OR
If you don't wan to get started with a WSDL extension...move your existing code into the "CreateServiceHost" method and scratch your custom host! That is not very nice but should work.
You didn't mention your host. IIS/WAS? If so, you may need to add the external host name to IIS config to the secure bindings list.
here is some information on changing the host-name in IIS hosted service
here is the command-line:
cscript //nologo %systemdrive%\inetpub\adminscripts\adsutil.vbs
set W3SVC/1/SecureBindings "10.(internal addr).1:443:ws.address.com"
"127.0.0.1:443:Internal Host name"
If that doesn't take care of it I'd go with "routeNpingme" and suggest you just need to get the endpoints specified correctly, using a binding that specifies your https ports and names. I think you can do this with existing binding options... but you may need to create a custom.
My guess is that the site bindings of the site that hosts your service in IIS are configured to only listen on hostname serv.internal.com instead of the external name ws.address.com.
If the IIS site bindings are configured with a hostname, that hostname is checked against all incoming URLs. Only URLs with matching hostname can be processed by that binding.
The firewall will redirect the request to the inside server, but will not alter the incoming URL string...

Categories