Auto detect internal/external development environment
Asked Answered
H

5

9

We use the following function to auto detect if we are on a machine internally or on a live server and then choose the appropriate configs for various components:

function devIsLocal(){

    $res=false;

    $http_host=$_SERVER['HTTP_HOST'];

    if($http_host=='localhost')$res=true;
    if($http_host=='127.0.0.1')$res=true;
    if(substr($http_host,-4)=='.lan')$res=true;
    if(strpos($http_host, '.')===false)$res=true;

    return($res);

}

As you can see it only relies on the HTTP_HOST value.

Of course, if you use some sort of virtual host locally like example.com then the function will be tricked.

Are there any other ways to fool the function? and what other variables/places could we peek at to determine where we are?

Halmstad answered 9/4, 2010 at 11:50 Comment(0)
D
12
'127.0.0.1' == $_SERVER["REMOTE_ADDR"]

This will never evaluate as TRUE on your live system. :)

Dishonesty answered 9/4, 2010 at 12:11 Comment(9)
It also won't if you access your local server with it's public-facing IP address, either.Edgardo
+1 for mentioning ip address checking. Andy is right, we'll also have to add the ip ranges reserved for intranets. Will this work then?Halmstad
@zaf: Yes, of course. Andy misunderstood it as a complete solution, while I just wanted to show the general concept. Maybe I have to be more explicit in future … :-/Dishonesty
@toscho You have to be more explicit otherwise you'll be whacked down by the horde. You live and learn.Halmstad
@toscho - you have to be explicit, either by tailoring your answer to the exact solution, or explaining the concept further - e.g. "Add additional checks for all IP addresses assigned to the server." Personally, however, I find this to be a "high-maintenance" solution, particularly if the server's IP addresses are managed by DHCP and dynamic DNS - not ideal, but it's possible and I've seen it done in a few environments.Edgardo
@toscho: ssh live-server -L80:127.0.0.1:80 will create a tunnel to the server (false positive); also, what if you are accessing the host over IPv6 (false negative)?Newsman
I use wamp. I guess, because of ipv6, the code does not work as is, the concept does work perfect. This is the modified code I use : if(($_SERVER["REMOTE_ADDR"] == '127.0.0.1') || ($_SERVER["REMOTE_ADDR"] == '::1')) {$environment = "development"; } else { $environment = "production"; }Arathorn
You have to be careful with CLI scripts trying to detect the environment with this and if a local script initiate a HTTP request locally your REMOTE_ADDR will in fact be 127.0.0.1.Richel
^^^ What @philix said. This will not work when the REMOTE_ADDR is null i.e. when the script is being executed via the command line.Overarch
E
16

Set an environment variable in your Apache virtual host configuration. This is the way the Zend Framework does it.

See the ZF quick start guide for an example (the "Create a Virtual Host" section.)

In your httpd.conf or a .htaccess file, put:

SetEnv APPLICATION_ENV "development"

Then in your application, use the getenv function to get the value:

$environment = getenv("APPLICATION_ENV");
if ($environment == "development")
{
    // do development stuff
}
else if ($environment == "live")
{
    // do live stuff
}
Edgardo answered 9/4, 2010 at 12:6 Comment(11)
Sometimes editing httpd.conf is not allowed. Having a .htaccess file that contains a key value to signify the environment is not automatic.Halmstad
@Halmstad - can you elaborate on "having a .htaccess file that contains a key value to signify the environment is not automatic"? I personally find this to be the most elegant solution, but hosts can restrict what you can do in the .htaccess file - is this what you mean?Edgardo
@Halmstad Setting a variable in .htaccess is as automatic as checking the HTTP_HOST variable, or adding it to the main configuration (httpd.conf, etc)Merchantman
@Andy/Cez I'm trying to find a method without modifying the environment the app lives. Not as elegant as your solution but its similar, I could just set a variable in the apps config but I want to avoid remembering to do yet another modification required for moving apps from testing to production (and vice versa). HTTP_HOST is 'automatic' because you don't have to do anything.Halmstad
@Halmstad are you always on a *nix server?Merchantman
@Halmstad - OK, I see your point now. I'm always trying to find ways to cut down on "maintenance" tasks and change risks. Anything that can change without your knowledge is a risk. This includes the hostname, IP address etc. Modifying the environment your app lives in is something that only changes with your knowledge - hopefully because you made the change! DHCP or a network admin can change a server's IP address without telling you. As you've already found, a user can fool your app by using a different virtual host name, a file on the filesystem can be deleted or quarantined.Edgardo
Continuing my previous comment... the config file solution is another option that keeps my "no risk" mantra, as long as you remember to update it every time you publish an update to your app, so it's not as fool-proof as the environment variable. Any reason why you're so against the environment variable approach? You want to modify the app's environment in some way so it knows it's running in live as opposed to dev, but then you say you don't want to modify the environment.Edgardo
@Cez nop. Add apple and ms to the mix.Halmstad
@Andy I don't want to modify the apps environment or its code in any way. The current devIsLocal() function achieves this and allows us the freedom to upload apps to a production environment without having to set a flag somewhere saying which environment its in. Of course, we still have configs for each environment but these get switched in depending on if devIsLocal(). For most apps this is a mission critical aspect and I would prefer code to decide this rather than someone remembering to update a setting.Halmstad
@Halmstad - you would only have to do this once when the application is first installed. Just replace the code in your existing function with a check of the environment variable, and you can then forget it exists if you want. You could also put your function in a library and use it for multiple apps - all you have to do is set the APPLICATION_ENV variable when you install the app and you're done. Minimum effort. 100% re-usable. No risk of compromise. As you've already pointed out, your existing function can be fooled, and you'd have to redeploy the app if the hostname changes.Edgardo
@Halmstad I'm still not clear why you can't set it to "production" as default and use the method that Andy and I have suggested to override when testing or developing. With regards to peeking at other variables, what about using shell_exec() to call the system hostname function?Merchantman
D
12
'127.0.0.1' == $_SERVER["REMOTE_ADDR"]

This will never evaluate as TRUE on your live system. :)

Dishonesty answered 9/4, 2010 at 12:11 Comment(9)
It also won't if you access your local server with it's public-facing IP address, either.Edgardo
+1 for mentioning ip address checking. Andy is right, we'll also have to add the ip ranges reserved for intranets. Will this work then?Halmstad
@zaf: Yes, of course. Andy misunderstood it as a complete solution, while I just wanted to show the general concept. Maybe I have to be more explicit in future … :-/Dishonesty
@toscho You have to be more explicit otherwise you'll be whacked down by the horde. You live and learn.Halmstad
@toscho - you have to be explicit, either by tailoring your answer to the exact solution, or explaining the concept further - e.g. "Add additional checks for all IP addresses assigned to the server." Personally, however, I find this to be a "high-maintenance" solution, particularly if the server's IP addresses are managed by DHCP and dynamic DNS - not ideal, but it's possible and I've seen it done in a few environments.Edgardo
@toscho: ssh live-server -L80:127.0.0.1:80 will create a tunnel to the server (false positive); also, what if you are accessing the host over IPv6 (false negative)?Newsman
I use wamp. I guess, because of ipv6, the code does not work as is, the concept does work perfect. This is the modified code I use : if(($_SERVER["REMOTE_ADDR"] == '127.0.0.1') || ($_SERVER["REMOTE_ADDR"] == '::1')) {$environment = "development"; } else { $environment = "production"; }Arathorn
You have to be careful with CLI scripts trying to detect the environment with this and if a local script initiate a HTTP request locally your REMOTE_ADDR will in fact be 127.0.0.1.Richel
^^^ What @philix said. This will not work when the REMOTE_ADDR is null i.e. when the script is being executed via the command line.Overarch
M
3

Adding to Andy Shellam's answer..

If you are using mod_vhost_alias, or have various domains with the same (virtual) document root, you can set the variable dependent upon parameters, e.g.

SetEnvIf SERVER_ADDR x.x.x.x APPLICATION_ENV=development
SetEnvIf HTTP_HOST abc.example.com APPLICATION_ENV=development
Merchantman answered 9/4, 2010 at 12:33 Comment(3)
Doesn't SetEnvIf use regex to evaluate the if conditon? If so, you'd need to escape the . wouldn't you?Hauteloire
@Lèsemajesté Admittedly the documentation shows escaped periods, but it seems to work fine either way whenever I have used SetEnvIfMerchantman
I suppose that makes sense, since the . would match any character, including a period, and because IP addresses follow a fixed format it wouldn't cause any other problems in this case.Hauteloire
N
1

Create and later look for a file that only exists on the live server's filesystem.

Granted, your environments should be as similar as possible; what I'm suggesting is something like this: in directory /var/environment/, have a file named {devel|test|qa|staging|live}, depending on the server you're on - then just check the filename.

Of course, you need to exclude this file from version control and from whatever build process you may have.

Newsman answered 9/4, 2010 at 11:58 Comment(3)
This is another avenue. Rather than creating a file (and then having to remember the fact) I thought of checking the filesystem for clues, like home directories etc, but sometimes the production servers file system is identical to the testing server.Halmstad
Indeed. I meant to create /var/environment/devel on devel, /var/environment/live on live, etc. These files don't need to have any content and they must not be used for anything besides distinguishing the environments. Edited my answer to clarify.Newsman
That's how I went with my most recent project. I simply created a file called development.txt and look for it in my config.php file. It's simple, explicit and requires no changes outside of the project directory.Dabney
D
0

Of course, if you have use virtual host locally like example.com then the function will be tricked.

Also if the host is not local but uses a widlcard or default vhost defn and the user adds the IP address to the hosts file locally.

I would recommend having a directory on the include path which also exists on live but is not replicated there - and simply store:

function getEnv(){
  return 'Live';
}

or

function getEnv(){
  return 'Test';
}

If both envs are on the same server - you can still do this by setting the include_path in Apache config or .htaccess.

This also allows you to seperate potentially sensitive env specific data - like database hosts/passwords and encyption keys.

C.

Diploma answered 9/4, 2010 at 11:59 Comment(1)
We could just check if a directory existed, for example the home directory of some user. Interesting idea. But sometimes a dedicated production servers filesystem layout mirrors the internal testing server.Halmstad

© 2022 - 2024 — McMap. All rights reserved.