Assign IPs to programs/processes - networking

I need to assign different IP addresses to different processes (mostly PHP & Ruby programs) running on my Linux server. They will be making queries to various servers, including the situation where processes connecting to the same external server should have different IPs.
How this can be achieved?
Any option (system wide, or PHP/Ruby-specific, using proxy servers etc) will suit me.

The processes bind sockets (both incoming and outgoing) to an interface (or multiple interfaces), addressable by IP address, with various ports. In order to have them directly addressable by different IP addresses, you must have them bind their sockets to different NICs (virtual or hardware).
You could point each process to a proxy (configure the hostname of the server to be queried to be a different proxy for each process), in which case the external server will see the different IPs of the proxies. Otherwise, if you could directly configure the processes to use different NICs for their communications, that would be ideal.
You may need to make changes to the code to make this configurable (very often, programmers create outgoing TCP connections with convenience functions without specifying the NIC they will use, as they typically don't care). In PHP, you can use "socket_bind" to bind the endpoint to a nic, e.g. see the first example in the docs for socket_bind.

As per #LeonardoRick request, I'm providing the details for the solution that I ended up with.
Say, I have a server with 172.16.0.1 and 172.16.0.2 IP addresses.
I set up nginx (on the same machine) with the configuration that was looking somewhat like this:
server {
# NEVER EXPOSE THIS SERVER TO THE INTERNET, MAKE SURE PORT 10024 is not available from outside
listen 127.0.0.1:10024;
# block access from outside on nginx level as well
allow 127.0.0.1;
deny all;
# actual proxy rules
location ~* ^/from-172-16-0-1/http(s?)\:\/\/(.*) {
proxy_bind 172.16.0.1;
proxy_pass http$1://$2?$args;
}
location ~* ^/from-172-16-0-2/http(s?)\:\/\/(.*) {
proxy_bind 172.16.0.2;
proxy_pass http$1://$2?$args;
}
}
(Actually I cannot remember all the details now (this code is 'from whiteboard', it's not an actual working one), nevertheless it should represent all the key ideas. Check regexes before deployment).
Double-check that port 10024 is firewalled and not accessible from outside, add extra authentication if necessary. Especially if you are running Docker.
This nginx setup makes it possible to run HTTP requests likehttp://127.0.0.1:10024/from-172-16-0-2/https://example.com/some-URN/object?argument1=something
Once received a request, nginx will fetch the HTTP response from the requested URL using the IP specified by the corresponding proxy_bind directive.
Then - as I was running in-house or open-source software - I simply configured it (or altered its code) so it would perform requests like the one above instead of (original) https://example.com/some-URN/object?argument1=something.
All the management - what IP should be used at the moment - was also done by 'my' software, it simply selected the necessary /from-172-16-0-XXX/ endpoint according to its business logic.
That worked very well for my original question/task. However, this may not be suitable for some other applications, where it could not be possible to alter the request URLs. However, a similar approach with setting some kind of proxy may work for those cases.
(If you are not familiar with nginx, there are some starting guides here and here)

Related

Map DNS entry to specific port

Let's say I have this DNS entry: mysite.sample. I am developing, and have a copy of my website running locally in http://localhost:8080. I want this website to be reachable using the (fake) DNS: http://mysite.sample, without being forced to remember in what port this site is running. I can setup /etc/hosts and nginx to do proxing for that, but ... Is there an easier way?
Can I somehow setup a simple DNS entry using /etc/hosts and/or dnsmasq where also a non-standard port (something different than :80/:443) is specified? Without the need to provide extra configuration for nginx?
Or phrased in a simpler way: Is it possible to provide port mappings for dns entries in /etc/hosts or dnsmasq?
DNS has nothing to do with the TCP port. DNS is there to resolv names (e.g. mysite.sample) into IP addresses - kind of like a phone book.
So it's a clear "NO". However, there's another solution and I try to explain it.
When you enter http://mysite.sample:8080 in your browser URL bar, your client (e.g. browser) will first try to resolve mysite.sample (via OS calls) to an IP address. This is where DNS kicks in, as DNS is your name resolver. If that happened, the job of DNS is finished and the browser continues.
This is where the "magic" in HTTP happens. The browser is connecting to the resolved IP address and the desired port (by default 80 for http and 443 for https), is waiting for the connection to be accepted and is then sending the following headers:
GET <resource> HTTP/1.1
Host: mysite.sample:8080
Now the server reads those headers and acts accordingly. Most modern web servers have something called "virtual hosts" (i.e. Apache) or "sites" (i.e. nginx). You can configure multiple vhosts/sites - one for each domain. The web server will then provide the site matching the requested host (which is retreived by the browser from the URL bar and passed to the server via Host HTTP header). This is pure HTTP and has nothing to do with TCP.
If you can't change the port of your origin service (in your case 8080), you might want to setup a new web server in front of your service. This is also called reverse proxy. I recommend reading the NGINX Reverse Proxy docs, but you can also use Apache or any other modern web server.
For nginx, just setup a new site and redirect it to your service:
location mysite.example {
proxy_pass http://127.0.0.1:8080;
}
There is a mechanism in DNS for discovering the ports that a service uses, it is called the Service Record (SRV) which has the form
_service._proto.name. TTL class SRV priority weight port target.
However, to make use of this record you would need to have an application that referenced that record prior to making the call. As Dominique has said, this is not the way HTTP works.
I have written a previous answer that explains some of the background to this, and why HTTP isn't in the standard. (the article discusses WS, but the underlying discussion suggested adding this to the HTTP protocol directly)
Edited to add -
There was actually a draft IETF document exploring an official way to do this, but it never made it past draft stage.
This document specifies a new URI scheme called http+srv which uses a DNS SRV lookup to locate a HTTP server.
There is an specific SO answer here which points to an interesting post here

Can nginx support URL based server blocks (VirtualHosts)?

Note: “VirtualHost” is an Apache term. NGINX does not have Virtual hosts, it has “Server Blocks”. (https://www.nginx.com/resources/wiki/start/topics/examples/server_blocks/).
I know about ip-based and name-based server blocks, but is it possible to have URL-based server blocks? In other words, I want http://example.com/foo and http://example.com/bar to be served from completely independent roots. This would be a trivial problem to solve with name-based server blocks if the names were different http://example1.com and http://example2.com, but since the names are the same (example.com) and only the path part of the URL is different... Can nginx support separate server blocks for these types of URL's?
See https://nginx.org/en/docs/http/request_processing.html
It seems the only options available are: IP address, port, host, and if none of those match, then it serves the default server. So, given that the name and port are the same in both cases, the only possible solution is to put a proxy server in front of nginx, and have the proxy server distribute to the backend nginx server using a different IP or port.

HTTP relay / proxy / mapping server

I'm trying to set up a simple relay / mapping server locally and feel there has to be some off the shelf solution, but I can't seem to find it.
I'm debugging an application of mine that needs to connect to host_A. Instead of connecting to host_A I want to configure it to connect to local_proxy. I don't want to use proxying protocols, but instead want to configure it to connect to http://localhost:80 and then have local_proxy connect to host_A and have local_proxy simply relay all messages back and forth.
I would expect to have to configure local_proxy to tell it what server it is supposed to relaying
Then there is 1 particular endpoint I want to be able to intercept and change the return info so I can better debug my application.
I thought I should be able to do this with Charles Proxy, but I couldn't figure out how.
At the moment, this doesn't need to support SSL (though that would always be nice).
I think what you are trying to build here is known as a reverse-proxy. There is a variety of solutions available for that, but you may produce results fastest with nginx, which can not only be configured for reverse proxy duties but is also sporting some powerfull SSL capabilities. A minimal solution to your problem would look like this:
server {
listen 80;
# Adjust for expected hostname. Space-separated list of hostnames possible.
server_name host_a;
location / {
# Forward all incoming requests to host_a
proxy_pass http://host_a;
}
}

Nginx catch "broken header" when listening to proxy_protocol

I need to use http health checks on a Elastic Beanstalk application, with proxy protocol turned on. That is currently not possible, and the health check fails with a an error --> *58 broken header while reading PROXY protocol
I figured I have two options
Perform the health check on another port, and setup nginx to listen to http requests on that port and proxy to my app.
If it is possible to catch the broken header errors, or detect regular http requests in the proxy_protocol server block, then redirect those requests to a port that listens to http.
I would prefer the latter(#2), if possible. So is there any way to do this?
Ideally, I would prefer not to have to do any of this. A feature request to fix this has been submitted to AWS, but it has no ETA.
The proxy protocol specification says:
The receiver MUST be configured to only receive the protocol described in this
specification and MUST not try to guess whether the protocol header is present
or not. This means that the protocol explicitly prevents port sharing between
public and private access. Otherwise it would open a major security breach by
allowing untrusted parties to spoof their connection addresses.
I think this means that option 2 is a sufficiently bad idea that it's not even supported by conforming implementations of the proxy protocol.
Option 1, on the other hand, seems pretty reasonable. You can set up a security group so that only legitimate health checks can come in on the port without proxy protocol enabled.
Another couple of options spring to mind too:
Simply point your health checks at the thing that's adding the header (i.e. ELB?), rather than directly at your Nginx instance. Not sure if this is possible with Elastic Beanstalk, it's not a service I use.
Use something else to add the proxy protocol header before forwarding the health-check traffic on to your Nginx, which would avoid having to duplicate your Nginx config. For instance a HAProxy running on the same machine as your Nginx could do this. Again, use security groups to ensure that only legitimate traffic gets through.

Is it possible to delegate an HTTP request transparently?

Suppose computer A sends an HTTP request to a server B, and B wants C to answer it. Is it possible for C to send a response back to A without B intervention and without specific actions from A (as with a 3xx redirection)? Suppose C may not have a public IP address.
That's what a reverse proxy would do. Depending what platform you are on, there are a lot of options.
One way that works on many platforms is e.g. node-http-proxy that you could start on server B. In the most simple case, this one-liner would do:
require('http-proxy').createServer(81, 'serverb').listen(80);
It listens on port 80 and redirects to port 81 on serverb.
See https://github.com/nodejitsu/node-http-proxy for more options.
Of course, there are lots of well-established proxies with a lot more bells and whistles (although node-http-proxy can do https tunneling etc. as well), but configuring those can be more challenging that running this one-liner. It all depends on your use case.
Edit: Reading your comment, this can be done using direct routing. Your question is about HTTP (layer 7), and as direct routing works on a lower layer, higher-level protocols like HTTP work as well. Quote from http://horms.net/projects/has/html/node9.html:
Direct Routing: Packets from clients are forwarded directly to the
back-end server. The IP packet is not modified, so the back-end
servers must be configured to accept traffic for the virtual server's
IP address. This can be done using a dummy interface, or packet
filtering to redirect traffic addressed to the virtual server's IP
address to a local port. The back-end server may send replies directly
back to the client. That is if a host based layer 4 switch is used, it
may not be in the return path.

Resources