Differentiate incoming requests by bound port using rails and puma - ruby-on-rails

I have a single rails app with two APIs --- one public and one private. Using puma, I have bound this app to ports 3000 and 3001. I have an internal IP address in my google VPC bound to the VM of the rails app on port 3000, and a public IP address bound to port 3001.
What I expected was to be able to understand which source a request came from based on that listener port, but requests only know about the port in the URL, so it's always listed at 80. All requests are coming through a GKE load balancer, so the REMOTE_IP header is always the same.
The only way I've been able to get my desired functionality is by starting two instances of the app, binding one port each, and including an environment variable.
Is it possible to differentiate requests based on the port they were bound to? This would allow me to use one instance of the app instead of two.

I realized I was looking in the wrong place. It doesn't make sense to generate HTTP headers when I was passing data within the same process. I found the data I wanted in the massive request object from inside application controller.
request.headers['puma.socket'].addr
# => ["AF_INET", 3001, "127.0.0.1", "127.0.0.1"]
So I have used this to implement the authorization I was looking for:
case request.headers['puma.socket'].addr[1]
when 3000 then :private
when 3001 then :public
else :unknown

Related

display domain instead of ip address as url

my website is www.somedomain.me, rented server from DigitalOcean, domian name is from Namecheap,
I redirected www.somedomain.me to http://104.236.xx.xx:6060/main/ ,
but when I open up www.somedomain.me it display the url as :
**http://104.236.114.32:6060/main/** or
**http://104.236.114.32:6060/main/resume.html** or
**http://104.236.114.32:6060/main/contact.html**,
how can i let it display as
**http://www.somedomain.me** or
**http://www.somedomain.me/resume.html** or
**http://www.somedomain.me/contact.html**
Thanks!
Lazy way?
Set up a URL Frame
Good way? The way the DNS gods intended?
If you have a dedicated IP, point it with an A record.
It's key to note A records can't specify a port. If at all possible, considering migrating your service to port 80? I understand sometimes this isn't possible but it'll make life a hell of a lot easier.
Edit: as for a redirect method, you could run a website on port 80 with like a line or two of HTML in an index.html to redirect to the proper port. Again assumes port 80 is unoccupied.
Previous post to clarify rewrite vs redirect
Michael makes a good point, however, in that if you want your customer to not have to include the port, you either have to do it for them via the redirect method you're using, or move your service to the default HTTP port 80. If you redirect the customer's browser to an IP address, that is what will be populated in the address bar and the Host header.
You might be able to do this thru Http Reverse Proxy. This wiki might guide you in the right track. You can use Heroku, Nginx, apache, etc. Though, apache might be overkill for the purpose.

ELB not routing traffic to healthy instance

This seems to have something to do with the subnet/availability zone, but I'm new to using a VPC and it's eluding me.
VPC: 10.80.0.0/16
subnet: 10.80.1.0/24 (us-east-1b)
subnet: 10.80.2.0/24 (us-east-1a)
All instances are Windows Server 2012.
I have an internet facing ELB created within my VPC (10.80.0.0/16). There is one instance added from AZ us-east-1a, which is on subnet 10.80.2.0/24. The instance is running IIS 7.5, with an app running on port 80 and /health.aspx set up for use as the ELB health check.
Internal traffic on the VPC is flowing normally (unrestricted). I can request health.aspx from this instance from another instance in us-east-1b (10.80.1.0/24). I can also copy files from one instance to another.
Outbound traffic is unrestricted. I can RDP to the instance (when connected to our VPN) and open a browser and request a web page and get it.
The ELB says the instance is healthy and I can see the requests to health.aspx in the IIS logs. Both the ELB and the instance are configured with a security group that allows 80 and 443.
But if I try to request {elb-url}/health.aspx over the open internet the request just times out. Similarly, with an elastic IP associated to the instance, a request to {elastic-ip}/health.aspx times out.
#Chris, thanks for the response...as it happens, I've already worked it out with some help from a friend. I'll post my findings here for posterity (in case anybody else was similarly confused about how ELB works).
This would be more clear with a diagram. But the summary is that in each availability zone, you need to create both a public and a private subnet. When you add availability zones to your ELB, you need to select the public subnet for the zone. This had already been done in us-east-1b before I got to this setup, and I had simply missed this nuance of ELB configuration. So for the new availability zone, I had to do this...
us-east-1c
private subnet 10.1.3.0/24 (using nat instance as default route)
public subnet 10.1.4.0/24 (using internet gateway as default route)
Then my instance goes in the private subnet as expected.
And the lynch pin of this whole thing is (drum roll....)
When I add us-east-1c to my ELB, I have to select the public subnet...10.1.4.0. Otherwise the instances will pass the health check (since the ELB can communicate with any instance within my entire VPC) but the responses from the servers cannot make it back out to the public internet.
This is what is so confusing. And I still don't fully understand it. The instance can make a request for, say, www.google.com. I can RDP to it and open a browser and get the web page. But a request from a host (like my laptop at my house) will die. strange.
PS: another note...make sure you are using enough NAT instance for your load. I think we ran into an issue where our NAT instance simply ran out of ports because too many web servers were trying to route outbound connections to 3rd party APIs through it. Quite honestly, I'm not good enough at this level of network/OS troubleshooting to be sure. But my theory is that our 8 instances of IIS were holding too many connections open to the NAT instance. We were also abusing the NIC on that micro instance. I upped us to two large instances, one per AZ and things smoothed back out. Both NAT instances are humming and we're not seeing the hung processes in IIS anymore.
Debugging this kind of issue is always a challenge. I have a few ideas to suggest based on what you have written (and generally apply to trying to solve this problem) that come from dealing with this a number of times.
Have you checked both the security groups and network ACLs? Bear in mind that all network ACLs need to be specified in both directions, as they are stateless. Also bear in mind that ELBs are a bit unique in this regard. While they are associated with your VPC, they sometimes need extra rules to ensure connectivity. In the past I have debugged this by opening all network ACLs on all ports, then removing these rules until it has stopped working in order to identify where the block was.
Security groups should be checked too. They are stateful but ensure that your load balancer has permissions to be hit from the web.
Have you checked this isn't an application configuration problem? I don't know how IIS comes out of the box but I would check it is setup to respond to all hostnames.
Check the ELB isn't an internal one, as that wouldn't be publically addressable.
You say the ELB is configured with the health check, but it's worth checking you also have the listener setup for port 80? It's in a separate tab on the dashboard and you will need this in addition to the health check for connectivity through the ELB.
Hope one of these tips is useful to you.

How to configure http://localhost:9000 to http:/mylocal.loc

I'm using the Play Framework which uses http://localhost:9000 by default. I'm also trying something with Twitter and it needs to use a callback url for authentication, but Twitter won't accept http://localhost:9000 as a callback URL.
How can I configure my localhost to map to something like http://mylocal.loc, (similar as with an Apache vhost), instead of http://localhost:9000?
The problem is that the URL needed to be entered in the following format:
http://127.0.0.1:9000/twitter-callback
The above works perfectly as a Twitter callback address.
Twitter isn't trying to access localhost directly, it simply takes the above address as far as I understand, sticks it into the HTTP response header, prompting whichever browser being used to perform a straight forward 302 redirect.
The following blog post had some invaluable information in regards to this question:
http://www.tonyamoyal.com/2009/08/17/how-to-quickly-set-up-a-test-for-twitter-oauth-authentication-from-your-local-machine/
The reason that twitter can't use localhost as a callback url is because localhost is a redirect to your computers loopback interface. In other words, localhost is always the computer that you're on. In order for other computers (including twitter) to access your host, you need to use an external IP address, or a hostname.
To get your IP address, visit whatsmyip. This will tell you your external IP address (which other computers on the internet can access). If you have a static IP address, you can purchase a domain name, or get a free one from something like no-ip or dyndns to make it easier to remember and type. You'll need to point a DNS record from that domain to your IP. You'll also probably need to do some port forwarding and stuff to get it to go to your computer on port 9000, rather than your router (dependent on your network setup).
Possibly an easier option would be to obtain a free hosting/domain service whilst you're testing.
EDIT: josef's problem was not related to the absence of internet access to his local server, see his own answer for what was going on and a solution. This answer handles the case where a local server needs to be visible from the internet.
localhost, aka 127.0.0.1 is the name that on each computer points to the computer itself. So Twitter looks at itself, obviously doesn't see the service, end of story.
If your computer is connected to a local network, most likely that network is NATed and using private addresses like 192.168.x.x, 10.x.x.x or 172.16x.x.x. These addresses are private (not known outside of the local network because not routed on the internet), so that doesn't help you either.
What remains is your public IP address, ie the address your router gets from your ISP. Via DNS you can map that address to a name, a free service that allows you to map a fixed name also to a variable address is DynDNS.
But wait, there is more! Your router protects your network by not allowing traffic originating OUTSIDE the private network IN, unless you define some forwarding rule in the router, in your case a rule that forwards incoming tcp traffic on port 9000 to your machine's port 9000.
Once all that has been taken care of, your computer will be accessible from the outside, and your callback should work.
Edit your hosts file and add the following line:
127.0.0.1 mylocal.loc
For Windows, it is located in C:\Windows\System32\drivers\etc\. On *nix, you can find it in /etc.

IP:Port mapping to a name

I have deployed multiple rails apps on a rackspace server running WEBrick. Each of them runs on a different TCP port.
I can access port 3000 using ptotem.com (my domain name).
I can access other apps using ptotem.com:3001, ptotem.com:3002..., but I would like to access them using appl_one.ptotem.com, app_two.ptotem.com.
How could I do this with DNS?
You can't really do that, the DNS naming mechanism and the TCP/IP port mechanism are two seperate, unrelated entities, which exist on different layers of the OSI model. If you think about it appl_one.ptotem.com:3000, appl_one.ptotem.com:3001 and appl_one.ptotem.com:3002 are still valid and would let you access your different apps.
You may be able to use SRV records if both your name server and your application support them, but support for this is currently sparse and flakey at best.
The other thing you could look into for HTTP applications is running them on the same port and using virtual hosts with something like this.
When I need to do stuff like this I usually set my NIC's to have multiple IP's (or add more NIC's if they don't support this) and have my apps bind to a specific IP, but honestly, it's very rare that you actually need to do it, for the sake of typing :port on the end of your connect strings...
On linux, you need to add a line to your /etc/hosts file.
for local host, testing, etc.
127.0.0.1 appname.com
or for private network
192.168.x.x appname.com
or public
210.32.197.4 appname.com

Why is request.env['REMOTE_ADDR'] returning two IPs?

When I visit my Rails 2.2 app on my remote server I receive the following value as my REMOTE_ADDR.
request.env['REMOTE_ADDR']: "75.184.124.93, 10.194.95.79"
What has me stumped is why there are two IPs. A quick check of my currently leased public IP confirms that my IP is 75.184.124.93.
So where is 10.194.95.79 coming from?
Is there something about how remote addresses are collected and reported in the HTTP headers spec that I'm missing? Is this expected, normal behavior?
It's definitely because of a reverse proxy.
Reverse proxies (I use BigIPs and Apache mod_proxy mode often) usually append all the intervening IPs to the list so you can pick out the right ones in your code.
For example, you might want to find the public one to log to your webstats application, so there it is right in the REMOTE_ADDR. But you also have the internal IP(s) so you know which loadbalancer it came from, which internal server its on for some kind of internal network tracking, etc

Resources