php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #66208 PHP-FPM Bind to address
Submitted: 2013-11-30 06:40 UTC Modified: 2013-12-03 10:56 UTC
Votes:10
Avg. Score:4.6 ± 0.7
Reproduced:9 of 9 (100.0%)
Same Version:2 (22.2%)
Same OS:2 (22.2%)
From: tronox at hotmail dot com Assigned:
Status: Open Package: *Network Functions
PHP Version: 5.5.6 OS: Amazon AMI
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2013-11-30 06:40 UTC] tronox at hotmail dot com
Description:
------------
While running multiple hosts on multiple ips:
I'm able to get a specific _SERVER["SERVER_ADDR"] for each host. However when using functions such as file_get_contents('example.com/whatismyip.php') it will always return the main IP of the server rather then the IP in _SERVER["SERVER_ADDR"].

My request is that either the IP used for functions such as file_get_contents is set to _SERVER["SERVER_ADDR"] or there be a setting that can be passed into PHP or PHP-FPM so that the outgoing IP can be set.

Seems that is _SERVER["SERVER_ADDR"] returns a specific IP that that should be used as default for outgoing traffic.

Expected result:
----------------
_SERVER["SERVER_ADDR"] be used as the default IP/Interface for outgoing PHP traffic.

Actual result:
--------------
_SERVER["SERVER_ADDR"] is ignored and the server's main IP is used instead.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2013-11-30 07:39 UTC] requinix@php.net
-Status: Open +Status: Wont fix
 [2013-11-30 07:39 UTC] requinix@php.net
A full explanation of why that's not possible is out of the scope of this bug reporting system, but suffice it to say that's not how networking works.
 [2013-11-30 08:36 UTC] tronox at hotmail dot com
MANY services out there allow for binding of their outgoing IPs. Even PHP does via scripting. I see no reason why php can't have a default IP binding setting. This would not only make it possibly more secure. But it would also support virtual hosting much better.

Making a VPM for every virtual host is a bit much. A Simple chroot suffices for most things packed with open_basedir. The only remaining issue is what IP clients are using for outgoing connections. Which opens a whole lot of security issues on a system. IE: I can't block or allow specific IPs outgoing traffic for specific ports. I can't track traffic usage reliably for a specific IP.
 [2013-11-30 09:14 UTC] requinix@php.net
So wait, are you talking about the IP address that your web server is binding to, which is what your title and latest reply are talking about (despite how it's not PHP-FPM that does that), or are you talking about the IP address that remote servers see your HTTP requests coming from, which is what your description was about?
 [2013-11-30 09:49 UTC] tronox at hotmail dot com
http://example.com/whatismyip.php code is:
<?php echo $_SERVER['REMOTE_ADDR'];?>

To explain it a bit better I guess. When I use file_get_contents('http://example.com/whatismyip.php') I get back the server's eth0 main IP. Rather then the IP, which while using phpinfo(), returned by _SERVER["SERVER_ADDR"].

Meaning the IP PHP is using to connect outside of the server is being defined by the server's default IP even though _SERVER["SERVER_ADDR"] is being set differently per host.

Now most of the functions in PHP already support a bindto via stream. However that would mean I would have to re-write code to bind to specific IPs. Not to mention having to babysit each site to make sure they were doing that as well.

I see no reason why PHP shouldn't, or at least have an option for it, bind outgoing connections defaultly to the IP set in _SERVER["SERVER_ADDR"].
 [2013-11-30 09:50 UTC] tronox at hotmail dot com
More specifically to your exact words:
"talking about the IP address that remote servers see your HTTP requests coming from".
 [2013-11-30 15:32 UTC] requinix@php.net
Keeping in mind that the SERVER_ADDR is not calculated by PHP (heck, it's not even part of the CGI standard), yes the SERVER_ADDR does reflect the interface through which the request came, but that interface is not necessarily one that can access another location.

A very typical development setup is a web server running and listening on localhost: attempting to access www.example.com from such a server could fail as PHP would try to bind to 127.0.0.1:0 (something that Ubuntu considers an "invalid argument"). Even if it did bind successfully that interface is useless for connections to anywhere except the machine itself. So okay, so then we make PHP ignore 127.* addresses and not attempt to bind at all should that be the REMOTE_ADDR?

Now consider the situation where a server sits between the LAN and WAN. That's two (not counting loopback) interfaces. Traffic from the WAN would allow for connections to www.example.com as that interface has a route to that host, but traffic from the LAN would fail because it does not. For PHP to know better it would have to be aware of the system's routing tables, and that's just becoming too much work - work that the OS does deliberately and automatically for us anyways.

The current default behavior of PHP, which is to use the default behavior in socket programming (ie, letting the operating system decide), works perfectly well for nearly all use cases; for those like you in unusual circumstances (I'm not even sure why you care about which interface is used for the connection so long as it works) the workaround of stream contexts and "bindto" does the job quickly and easily.

All that aside, it sounds like your real problem is that the boxes aren't using the right interfaces for the right destinations, and that's totally something that should be solved with the network configuration on the machines themselves.
 [2013-12-01 02:53 UTC] tronox at hotmail dot com
The network interfaces have routes in place to use their bound IPs as the default outgoing IPs.

However while running PHP through PHP-FPM even binding it to the specific IP.
My setup is 2 PHP-FPM pools, one running on 10.0.0.5:9005 and another running on 10.0.0.6:9006. Even though they are bound to listen on those IPs/Ports they still send out requests using the server's main IP 10.0.0.5.

0:      from all lookup local
32762:  from all to 10.0.0.6 lookup cldmv
32763:  from 10.0.0.6 lookup cldmv
32764:  from 10.0.0.7 lookup 10001
32765:  from 10.0.0.6 lookup 10001
32766:  from all lookup main
32767:  from all lookup default
default via 10.0.0.1 dev eth0
default via 10.0.0.1 dev eth1  metric 10001
10.0.0.0/24 dev eth0  proto kernel  scope link  src 10.0.0.5
10.0.0.0/24 dev eth1  proto kernel  scope link  src 10.0.0.6
169.254.169.254 dev eth0
[root@s1 ~]# netstat -anr
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         10.0.0.1        0.0.0.0         UG        0 0          0 eth0
0.0.0.0         10.0.0.1        0.0.0.0         UG        0 0          0 eth1
10.0.0.0        0.0.0.0         255.255.255.0   U         0 0          0 eth0
10.0.0.0        0.0.0.0         255.255.255.0   U         0 0          0 eth1
169.254.169.254 0.0.0.0         255.255.255.255 UH        0 0          0 eth0

As you can see above all traffic from 10.0.0.6 is to use eth1. Which it does with everything else installed so far. However with PHP it's only using the server's root main default IP. Even though there is a default eth1 and the ip 10.0.0.6 is routed to use eth1.

I care about what interface a client's outbound traffic is on is due to bandwidth monitoring. Not to mention if I can send those functions a bindto then why is it so much to ask for a way to defaultly bind their outgoing connections? I'm not asking for a restriction or anything. Most users will just use those functions as in and will never know of the default binding (if one is implemented). However in it's current state bandwidth will show up on the client running 10.0.0.5 for all traffic outgoing through php on the box.

I've been able to lock down every system so far to allow users to only access their section of the server through their IP. PHP seems to be the last string in the whole thing with it's inability to bind defaultly to the IP used to connect. Yea I could block all outbound traffic on 10.0.0.5 for each user but that could be a lot of IP table rules and then would force users to modify their code to bind to a specific IP fr each one of these functions. Which while there is a workaround available. 

PHP is already creating a context to connect out, I can't see it being that hard to tell it an ip to bind to. Keep in mind this is coming from a server administrator, not simply a user. 

As you can see below the IPs are nearly always the main server's IP. The only interesting part is when I hit the local IPs which always reply with the ip of the ip being pinged.
http://test.cldmv.net/test.php
http://test2.cldmv.net/test.php
 [2013-12-01 14:48 UTC] requinix@php.net
-Status: Wont fix +Status: Open
 [2013-12-01 14:48 UTC] requinix@php.net
But that only affects inbound connections - when outbound ones are created, be that by PHP or something else, the OS still decides which interface to use unless told otherwise. Again, it's not PHP that's deciding to use eth0 but the system. And that's the problem you're having: it's not picking the interface you want it to pick, but your solution is to somehow force PHP (who, once more, is not the one deciding) to "say otherwise". Then again, you've managed to "lock down every system so far", which I must not understand because what I would expect that to entail isn't even possible.

But whatever, if there is a change that should be made it's not my call how it'd be done.
 [2013-12-03 10:56 UTC] tronox at hotmail dot com
Basically my suggestion is to allow a bindto setting in the php.ini to defaultly tell PHP what IP to bindto. Since it's clearly not following the ip route and ip rule settings (at least in my enviroment). This way each php-fpm could be told what IP to use and thus allow for traffic mapping for the end user.

This could be an issue with Amazon's AMI but I highly doubt it as it's based routely on Fedora. And the protocols for ip route and ip rule are "tested" and "old". Either way a simple setting for this would allow for system administrators to work around issues as such. Without forcing their users to re-write code.
 [2013-12-13 13:09 UTC] gabri dot ns at gmail dot com
it would be great if there is a configuration to set this setting globally. something like: ini_set('socket_outbound','127.0.0.1');
 [2014-03-12 15:39 UTC] daan at djunity dot nl
I second this request.
The issue we see alot it with server that have both ipv4 and ipv6 running.
Outgoing requests are going with ipv6 instead of the ipv4 shared server ip.
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Thu Jul 18 21:01:25 2019 UTC