I have a site with a fixed IP address, and I make c# calls to backend data server methods via HttpWebRequest. This backend system will be set to only permit incoming requests from my site's IP fixed address.
Is there a way to set the IP address of the HttpWebRequest to my site's IP (I suspect my cloud host or .net is somehow permitting other IPs being used)?
I'm not trying to spoof an IP; I want ensure that my asp.net code uses the site's own dedicated IP, or at least check to see what IPs it may be using when it makes requests.
Use the HttpWebRequest.ServicePoint.BindIPEndPointDelegate property:
request.ServicePoint.BindIPEndPointDelegate = delegate
{
return new IPEndPoint(IPAddress.Parse("10.0.0.3"), 0);
};
Example:
using System;
using System.Net;
using System.IO;
class Program
{
public static void Main ()
{
var request = (HttpWebRequest)HttpWebRequest.Create ("http://smsc.vianett.no/ip/");
request.ServicePoint.BindIPEndPointDelegate = delegate { return new IPEndPoint(IPAddress.Parse("YOUR_IP_HERE"), 0); };
var response = (HttpWebResponse)request.GetResponse ();
Console.WriteLine (new StreamReader (response.GetResponseStream ()).ReadToEnd ());
}
}
Related
I've read a lot of conflicting information about this and it seems people are not 100% clear on what is possible and what is not. I am certain that you cannot host a gRPC server app in IIS due to the HTTP/2 limitations. The documentation is pretty clear. However, I want to use IIS as a reverse proxy, with the internal side communicating using gRPC. So the client would be in IIS, not the server. I assumed that since the communication at this point (i.e. the back end) was not funneled through IIS, there would be no issue with this. However, I keep seeing mixed answers.
I have created a dumb webapp that is hosted in IIS Express and can successfully post to my service running on Kestrel with gRPC.
Client code sample below. The SubmitButton is just a form post on the razor page.
public async void OnPostSubmitButton()
{
// The port number(5001) must match the port of the gRPC server.
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(
new HelloRequest { Name = "GreeterClient" });
Console.WriteLine("Greeting: " + reply.Message);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
Server code is the boilerplate template for gRPC but looks like this:
namespace grpcGreeter
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
// Additional configuration is required to successfully run gRPC on macOS.
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
namespace grpcGreeter
{
public class GreeterService : Greeter.GreeterBase
{
private readonly ILogger<GreeterService> _logger;
public GreeterService(ILogger<GreeterService> logger)
{
_logger = logger;
}
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
return Task.FromResult(new HelloReply
{
Message = "Hello " + request.Name
});
}
}
}
This works. But, because I keep seeing mixed information saying it that it won't, I am not certain that once I go to deploy the client code (i.e. the reverse proxy), if I will run into problems. I would like to use a host like Azure...but don't know if it's possible or not.
Any clarity on the subject would be greatly appreciated.
As far as I know, we could use asp.net core mvc or razor page application as the client to call the grpc server.
But gRPC client requires the service to have a trusted certificate when you hosted the application on remote server IIS.
If you don't have the permission to install the certificate, you should uses HttpClientHandler.ServerCertificateCustomValidationCallback to allow calls without a trusted certificate.
Notice: this will make the call not security.
Additional configuration is required to call insecure gRPC services with the .NET Core client. The gRPC client must set the System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport switch to true and use http in the server address.
Code as below:
AppContext.SetSwitch(
"System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
var httpClientHandler = new HttpClientHandler();
// Return `true` to allow certificates that are untrusted/invalid
httpClientHandler.ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
var httpClient = new HttpClient(httpClientHandler);
var channel = GrpcChannel.ForAddress("https://localhost:5001",
new GrpcChannelOptions { HttpClient = httpClient });
var client = new Greeter.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = "World" });
I have grpc dotnet core app, dotnet core version 2.1.
App runs fine when executed on local maschine and accessed from that maschine. When I want to access it from other machine (althought firewall disabled, port opened) I can't access it. I think it is something to the code. I'm not dotnet developer but have some microservice legacy code:). Can it be that problem is in Grpc.Core Server class?
Please help out
Code:
using System;
using System.Collections.Generic;
using System.IO;
using Grpc.Core;
...
static void Main()
{
const int port = 4000;
const string host = "localhost";
var cert = File.ReadAllText("cert.pem");
var key = File.ReadAllText("key.pem");
var keypair = new KeyCertificatePair(cert, key);
var server = GetServer(port, host, keypair);
server.Start();
server.ShutdownTask.Wait();
}
private static Server GetServer(int port, string host, KeyCertificatePair keypair)
{
return new Server
{
Services = { SomeService.BindService(new SomeServiceImpl()) },
Ports = { new ServerPort(host, port, new SslServerCredentials(new List<KeyCertificatePair>
{
keypair
}))}
};
}
Your code explicitly says const string host = "localhost"; which means that it will only listen on the loopback interface (= not accessible from other machines). Use e.g. "[::]" or "0.0.0.0" to listen on all network interfaces.
I need to verify that a server is trusted in the machine root certificate store but need to accommodate the scenario that a bridge CA could be used.
According to MSDN, this method of using a TCPClient, then opening the socket seems to be the most preferred way to inspect a SSL Stream's certificate.
When my function hits the ValidateServerCertificate function, I intend to inspect the chain object to determine if a Root certificate is stored in the trusted root certificate store on the computer. Easy enough.
The complexity (and lack of knowledge) comes in when I need to follow a
"bridge certificate" that is used to cross sign multiple PKI trees. I'm unsure if the bridge certificate will appear in the local store, the chain, or some other place (if at all).
Furthermore I'm unsure how to follow the branching logic that may occur, since the bridge can occur at any level of the tree.
Suggestions, direction, or a flowchart is welcome
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
namespace sockittome
{
class Program
{
static void Main(string[] args)
{
string machineName = "pesecpolicy.bankofamerica.com";
// Create a TCP/IP client socket.
// machineName is the host running the server application.
TcpClient client = new TcpClient(machineName, 443);
// Create an SSL stream that will close the client's stream.
SslStream sslStream = new SslStream(
client.GetStream(),
false,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
null
);
// The server name must match the name on the server certificate.
try
{
sslStream.AuthenticateAsClient(machineName);
}
catch (AuthenticationException e)
{
Console.WriteLine("Exception: {0}", e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
}
Console.WriteLine("Authentication failed - closing the connection.");
client.Close();
return;
}
}
// The following method is invoked by the RemoteCertificateValidationDelegate.
public static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
// How do I verify the root certificate is installed?
// What is the simple way (check certificate hash in the computer store)?
// What is the complete way (look for bridge certificates ?????)
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
// Do not allow this client to communicate with unauthenticated servers.
return false;
}
}
}
How can I send a custom response upon a custom request on a xampp/wamp based Apache server upon a connection to a specific port?
I'm trying to reply to the \0 a flash app is requesting in order to allow a crossdomain http GET request.
The flash policy request, is made to port 843 by default and i'd like to keep it that way.
The port should get a \0 (ending with a null char,\0 is just for the reference) and replying with something like:
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<site-control permitted-cross-domain-policies="master-only"/>
<allow-http-request-headers-from domain="*" headers="*" secure="true" />
<allow-access-from domain="*" to-ports="*" />
</cross-domain-policy>
As far as i know, the request should be return as a plain text, although Content-type, might be needed as well.
I've tried using the following: http://socketpolicyserver.com/ and although it listens to the port and accepts connections, it doesn't reply with the specified xml upon request.
Any methods / ways of achieving a proper reply will be appreciated,
with regards,
Mike.
!---UPDATE--->
I wrote a simple C# web server which listens to port 843, and serves the aforementioned policy - it worked out just fine, however, when using a SecureSocket connection for a secure connection (i.e opening a socket to a HTTPS/SSL protocol) - the request that is sent is encrypted using the hosts certificate. As far as i know, there's no way of listening or acquiring the servers certificate and decrypting the data via an external app hence, the only way is to somehow 'teach' Apache to respond with the crossdomain policy after a proper request is sent via an appropriate port.
Another idea i have is to read the server's certificate file stored in the Apache directory regardless of what happens on the server itself, though imo it's an overkill.
Would love to hear your comments,
Mike.
So here's how i eventually solved it:
I've used this guys code with some modifications: http://www.switchonthecode.com/tutorials/csharp-tutorial-simple-threaded-tcp-server
and created a simple multithreaded web server that listens to port 843, and provides a somewhat general policy upon the appropriate flash request.
There were several examples provided by Adobe, but for some reason, windows didn't like those.
Also note that if you're using the SecureSocket object of flash it should allegedly use the target servers (IIS'/Apaches'/Tomcats' etc..) SSL credentials and will initiate a client Authentication using the public key of the target servers certificate, then again, it might not so this code doesn't have SSL support, although i've started implementing one using C#'s SSL Streams, so far without any luck. If you can make it work via SSL, please let me know.
Hope this code will help,
Mike.
using System;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO;
namespace TCPSexyServer
{
class Server
{
private TcpListener tcpListener;
private Thread listenThread;
private void ListenForClients(int p)
{
throw new NotImplementedException();
}
public Server()
{
this.tcpListener = new TcpListener(IPAddress.Any, 843);
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
}
private void ListenForClients()
{
this.tcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
TcpClient client = this.tcpListener.AcceptTcpClient();
//create a thread to handle communication
//with connected client
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
}
}
private void HandleClientComm(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
while (true)
{
bytesRead = 0;
try
{
//blocks until a client sends a message
bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
//a socket error has occured
break;
}
if (bytesRead == 0)
{
//the client has disconnected from the server
break;
}
//message has successfully been received
UTF8Encoding encoder = new UTF8Encoding();
string sentData = encoder.GetString(message, 0, bytesRead);
Console.WriteLine(sentData);
if (sentData == "<policy-file-request/>\0")
{
String policy = "<?xml version=\"1.0\"?>\n" +
"<!DOCTYPE cross-domain-policy SYSTEM \"/xml/dtds/cross-domain-policy.dtd\">\n" +
"<cross-domain-policy>\n" +
"<site-control permitted-cross-domain-policies=\"master-only\"/>\n" +
"<allow-http-request-headers-from domain=\"*\" headers=\"*\" secure=\"true\" />\n" +
"<allow-access-from domain=\"*\" to-ports=\"*\" />\n" +
"</cross-domain-policy>\0";
byte[] buffer = encoder.GetBytes(policy);
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
Console.WriteLine(policy);
}
else
{
tcpClient.Close();
}
System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
}
tcpClient.Close();
}
public static void Main(string[] args)
{
Server blah = new Server();
}
}
}
I'm trying to set up a WCF service hosted in IIS in ASP.Net compatibility mode that is protected via Forms Authentication and accessed via a .Net User Control in IE. (see Secure IIS hosted WCF service for access via IE hosted user control).
The User Control in IE is needed because it uses a specific third-party control for which there doesn't exist anything comparable in Silverlight or AJAX.
So I need the UserControl to set the authentication and session id cookies in the http request headers before it accesses the WCF service. My approach is to set up a Message inspector that does this.
So I've defined the Message Inspector:
public class CookieInspector : IClientMessageInspector {
public void AfterReceiveReply(ref Message reply, object correlationState) {
}
public object BeforeSendRequest(
ref Message request,
IClientChannel channel) {
HttpRequestMessageProperty messageProperty;
if (request.Properties.ContainsKey(HttpRequestMessageProperty.Name)) {
messageProperty = (HttpRequestMessageProperty) request.Properties[
HttpRequestMessageProperty.Name
];
}
else {
messageProperty = new HttpRequestMessageProperty();
request.Properties.Add(
HttpRequestMessageProperty.Name,
messageProperty
);
}
// Set test headers for now...
messageProperty.Headers.Add(HttpRequestHeader.Cookie, "Bob=Great");
messageProperty.Headers.Add("x-chris", "Beard");
return null;
}
}
and an Endpoint behaviour:
public class CookieBehavior : IEndpointBehavior {
public void AddBindingParameters(
ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters) {
}
public void ApplyClientBehavior(
ServiceEndpoint endpoint,
ClientRuntime clientRuntime) {
clientRuntime.MessageInspectors.Add(new CookieInspector());
}
public void ApplyDispatchBehavior(
ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher) {
}
public void Validate(ServiceEndpoint endpoint) {
}
}
and I configure and create my channel and WCF client in code:
var ea = new EndpointAddress("http://.../MyService.svc");
// EDIT: Http cookies can't be set with WSHttpBinding :-(
// var binding = WSHttpBinding();
var binding = new BasicHttpBinding();
// Disable automatically managed cookies (which enables user cookies)
binding.AllowCookies = false;
binding.MaxReceivedMessageSize = 5000000;
binding.ReaderQuotas.MaxStringContentLength = 5000000;
var cf = new ChannelFactory<ITranslationServices>(binding, ea);
cf.Endpoint.Behaviors.Add(new CookieBehavior());
ITranslationServices service = cf.CreateChannel();
However when I look at my request with Fiddler, the http header and cookie aren't set, and I have no clue why. I've read various articles on the Net, Stackoverflow etc that basically say that it should work, but it doesn't. Either I'm missing something obvious, or there's a bug in WCF or something else?
Well I figured it out, if I use a basicHttpBinding instead of a WSHttpBinding it works. No idea why though...
WSHttpBinding may be composed of more than one physical message to one logical message. So when successive physical calls are made, they may not be carrying the cookie appropriately