SavonClient not able to access wsdl through proxy server - ruby-on-rails-4

Context : I have to call an externalService, which lies outside the environments hosted by us on AWS-EC2. This externalService requires IP Addresses to be whitelisted before accessing it. Since EC2 hosts IPAddresses are not guaranteed to be same and can change while replacing hosts, we decided to route the API calls through a proxy-server.
We are doing the same for some other externalServices calls as well, but those are all REST based, so we have not faced any problems while calling their APIs using rest-client or net/http.
Now, this time it's a SOAP Service and we are using Savon to access it.
I am able to download the url using "curl" on proxy server host but if I access wsdl through proxy-server from SavonClient, it fails. It gives 403 forbidden error.
irb(main):102:0* client = Savon.client do |variable|
irb(main):103:1* variable.proxy 'http://172.31.50.91:3128'
irb(main):104:1> variable.wsdl 'https://<some_url_here>'
irb(main):105:1> end
=> #<Savon::Client:……>>>
irb(main):106:0>
irb(main):107:0* client.operations
Net::HTTPServerException: 403 "Forbidden"
For other services which do not require IP whitelisting SavonClient works, whether or not proxy-server address is provided.
Any help will be appreciated. I have been struck here for long.
Thanks,

Related

Hostname/IP does not match certificate's altnames

I'm working on a REST API that itself makes requests to another REST API -- basically, it provides a more convenient interface and also some extra functionality. Let's call my REST API X and the REST API to which my API calls Y.
Whenever I make requests to the endpoints of Y on my machine with cURL, REST Client, etc; all requests are successful. Like I mentioned, my API X is acting as a wrapper to Y, so when I upload my API to aws Lambda and create the respective endpoints in API Gateway, when I make a request to one of the endpoints I get this message:
Hostname/IP does not match certificate's altnames: Host:
X.execute-api.us-west-2.amazonaws.com. is not in the cert's altnames:
DNS:somehostname.com
So far, I have uploaded two lambdas with their respective endpoints, and the problem above only seems to be happening for one of the endpoints (the request to the other endpoint happens without problem).
I would like to know why this is happening and if this is a problem on my side? Meaning, is there something I am forgetting or something I can do -- except bypassing some security mechanism -- to fix this on my side? Whenever I make requests to the original API Y on my machine I'm not getting any errors so I'm a bit puzzled by this.
I think you're missing how SSL certificates work. Depending on how the certificate is setup for "API Y" you can't just connect to a different server and have it work. While you are conceptually a proxy to the real back end from the client perspective, you're a totally different host and the SSL certificate is for "API Y" only.
This is the same reason that you can't decide that you want to have an API named trustme.google.com - you don't have control over the google.com domain (presumably).
If there is a way to change the hostname that your client connections are using (to something like proxy.yourdomain.tld) then you can setup an SSL certificate for that domain and things should work. However, at that point you may run into CORS issues - post again if you have that issue.
AWS documents how to setup your own SSL certificate for API gateway. It's pretty easy though if you have an existing certificate you may need to use the AWS certificate manager to get a (free) certificate for your API.
Update 03/10/2022: Before your proxy hands off the request to the real backend service, make sure to set Host header to the hostname of the real service, see here.
I also developed a HTTP client -> APIG endpoint -> Lambda -> Host application, where the Lambda acts as a proxy between the client and the 3rd party Host. My Lambda is written in Node.js. I was getting this same exact error when the Lambda tried to invoke the 3rd party Host,
{
"statusCode": 500,
"body": "Hostname/IP does not match certificate's altnames: Host: zyxfghsk.execute-api.us-east-1.amazonaws.com. is not in the cert's altnames: DNS:*.somehost.com, DNS:somehost.com"
}
My setup uses Lambda Proxy integration with APIG, and I pass the set of HTTP headers from the client as-is to the 3rd party Host. I noticed the headers contained header Host: zyxfghsk.execute-api.us-east-1.amazonaws.com, which I think comes from the client. So in the Lambda code, right before passing the request to the 3rd party Host, I just simply delete the Host header from the request, and the problem went away. Another approach I was trying earlier, which also works, but not as ideal is that I was setting NODE_TLS_REJECT_UNAUTHORIZED=0 in the Node.js environment, which effectively disables SSL certificate validation by Node.js.
I believe, though not 100% certain, that in my case at least the error was getting thrown by Node.js certificate validation.
Update 03/10/2022: Before your proxy hands off the request to the real backend service, make sure to set Host header to the hostname of the real service, see here.

Allowing REST URL within network, and blocking outside traffic

Assuming my REST API URL is
http://myshop.com/rest/api/product/1
I would like to have this return data only when calling it within the corporate network, everyone else should not get any result back.
Here are the use cases where they can/cannot be accessible
User accessing it from outside the network but using it via a JSF/CDI application deployed on JBoss Server. (Should be accessible)
User directly accessing the URL from inside the network (via rest client or directly typing the url in browser window) (Should be accessible)
User directly accessing the URL from outside the network (via rest client or directly typing the url in browser window) (Should NOT be accessible)
Thanks for taking a look.
I'd suggest to get an IP address from the request and then check it via permitted IP's or mask of a subnet. How to get an IP address if you're using JAX-RS API you can find here: How to find out incoming RESTful request's IP using JAX-RS on Heroku?
Another option it's of course to block incoming request by firewall or by server's setting.

How to use HTTPS for webservice and android app?

Im working on some JSON-based web service that is supposed to work with Android application.
I would like to encrypt data transport between client (android) and server (virtual server in datacenter).
I don't have to make sure that my server is my server, just data encryption.
I have no idea how to use HTTPS.
Do I just put my PHP files in private_html and use https://example.com url?
To use HTTPS, you don't have to do anything in the coding of your web service - it's all in your hosting. Here the are steps you can follow. The specific instructions differ in your hosting (IIS, Apache, AWS/Azure, etc), but you can google specifics on how to accomplish any of these steps for whatever host and application framework you decide.
Buy an SSL certificate (there are many different vendors, but expect between $75-$200 for the certificate) based on the vendor, reputation, and level of security you need.
Generate a certificate signing request (CSR) from the server you'll be hosting.
Upload the CSR to the SSL vendor who will validate and provide the certificate for your use.
Import the SSL certificate into your application server, and configure the site to use the certificate. For instance, if you're hosting Microsoft IIS, you'd import the SSL certificate and then add HTTPS bindings on 443 to the specific website hosting your web service.
Another point of security. Since you are deploying SSL, you don't have to do any application level encryption (assuming you are not putting sensitive information in query strings - use POST if you think you need to). You probably would want to implement some security to restrict access to your web service so only your app can access it. Best practice is some level of OAuth, but at a minimum some type of pre-shared key in the header of the request is a lot better than nothing.
Here are some additional sites for more information:
https://www.digicert.com/ssl-certificate-installation.htm
https://support.godaddy.com/help/category/742/ssl-certificates-installing-ssl-certificates?prog_id=GoDaddy
If you don't want to pay for a certificate, you can use certificate signet by your own CA and add the root certificates into your application using HTTPClient and keystores
Here there's some guides
http://datacenteroverlords.com/2012/03/01/creating-your-own-ssl-certificate-authority/
http://developer.android.com/reference/org/apache/http/client/HttpClient.html
KeyStore, HttpClient, and HTTPS: Can someone explain this code to me?
http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/
You can limit users to use JUST and only HTTPS in apache, IIS or whatever do you use. If your client connects to your server, his communications will be likely to encrypted, because he is already using HTTPS. And for responsing in HTTPS you virtually cannot send HTTPS responses, as far as I know, unless that other side isn't also a website (for example, if you have your website, you could send such a response e.g. to Google). You should be okay to send data like http status codes (OK, NotModified, PageNotFound, ...), or if you want something more, or if it is a requirement, then there you still have JSON and you could encode it as well, with some encoding algorithms, or use binary JSON format.
Check if your hosting company provides a free public shared https address. Most of them do.
If you want to understand how to do it right, follow this thread
Warning: Don't stick with the solution below for production.
If you plan o use an https endpoint without a certificate you have to make sure to disable peer verification, check this answer

publish jax-ws service on public ip

I've a simple jax-ws web service that on localhost works fine with the clients, but now I want to publish the web service on a public ip, so the clients can interact with it through wan network instead lan network.
I signed to no-ip dns service provider and defined my host like "myname.no-ip.info".
In my code i start the service in this way:
Endpoint.publish("http://localhost:8080/baseStationProvider", new BaseStationProvider());
and the browser at http://myname.no-ip.info:8080/baseStationProvider#wsdl doesn't show the wsdl.
If i start the service in this way:
Endpoint.publish("http://myname.no-ip.info/baseStationProvider", new BaseStationProvider());
compiling the code, it raise this exception:
Server Runtime Error: java.net.SocketException: Unresolved address
Any idea to problem and/or how to do what i need?
thanks in advance
This seems likely to do with routing and firewalls as opposed to web service publication etc. Ignoring the web-service aspect, can you even reach your server when you use this in your browser:
http://myname.no-ip.info:8080/
?
Which should look the same as
http://localhost:8080/
If not, then it is probably DNS/Routing/Firewalls that you need to check. Diagnostics that will help there are:
1) can the machine you are testing on resolve the DNS name mynam.no-ip.info? nslookup, ping, traceroute/tracert
2) is there a firewall blocking port 8080 from reaching local host? If the route from the internet to your host hits a firewall (which it will) that firewall will have to forward the request.
Good luck.

Coldfusion REST API returning 'connection failure. status code unavailable.' when called remotely

Now I want to prefix this with I am unsure whether this should be here or on server fault so I'll post it here to begin with.
We are developing a REST API in Coldfusion 9 which is being hosted on IIS 7 for a client which on certain calls must call another internal webservice. When making these calls from the server that the API is hosted on there are no errors, however when we make the call from a remote machine the following message is returned:
'connection failure. status code unavailable.'
I have googled the issue where the following was suggested as a fix http://www.talkingtree.com/blog/index.cfm/2004/7/28/20040729 but it does not work for me. The following are the notes from my own testing:
The calls which are causing the issue are a mix of get, put, post and delete.
The only common part of each call is the talking to the same webservice.
I am able to call the webservice directly from the places where I make the remote calls to the API from.
While all the calls make a call to the same webservice they do not all make the same call to the webservice.
Ideas:
Call the web service remotely using a URL that resolves to the internal ColdFusion server (e.g. http://[servername]:8300). You will probably have to some configuration to get this working. Reason for doing so is to identify if IIS or ColdFusion is the root cause. If you can hit the internal server remotely then IIS is probably the issue.
Is the web service call over SSL? If so, is remote caller also ColdFusion? If you've answered both yes, check to see if the SSL certificate is trusted by the remote caller's JVM. If not, you need to register it as part of the JVM's keystore.
Can you ping the host server from the caller? If not, does the caller need a "hosts" entry?
If enabled on your host, review the .NET filter and how it interacts with HTTP calls. I've experienced a situation where I was unable to access a folder called "/bin" because the .NET filter intercepted the requests.
The issue turned out to be that each of the calls required basic authentication which the calls themselves were handling, however basic authentication had been turned on in IIS which was causing IIS to intercept any requests with an Authorization header.
This causes an issue as IIS assumes that if authentication is passed up then it is for a user on that machine/domain and would reject any other credentials (which were valid for the system). It was working when we were logged into the machine because it was coming locally it did not need to authenticate the user.