Last active
May 9, 2017 18:18
-
-
Save typhonius/7524395 to your computer and use it in GitHub Desktop.
An include file for the Acquia Cloud to be placed at the docroot/sites/acquia.inc location. May be optionally called from the site's settings.php with further details found on the Acquia Knowledgebase article.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @file | |
* Utilities for use in protecting an environment via basic auth or IP whitelist. | |
*/ | |
function ac_protect_this_site() { | |
global $conf; | |
// Test if we are using drush (command-line interface) | |
$cli = drupal_is_cli(); | |
// Is the user on the VPN? Default to FALSE. | |
$on_vpn = $cli ? TRUE : FALSE; | |
if (!empty($_SERVER['AH_Client_IP']) && !empty($conf['ah_whitelist'])) { | |
$on_vpn = ah_ip_in_whitelist($_SERVER['AH_Client_IP'], $conf['ah_whitelist']); | |
} | |
// Should we skip the auth check? Default to FALSE. | |
$skip_auth_check = FALSE; | |
// Check if we should disable cache for this page. | |
if (ah_path_no_cache()) { | |
$conf['page_cache_maximum_age'] = 0; | |
} | |
// Check if we should skip auth check for this page. | |
if (ah_path_skip_auth()) { | |
$skip_auth_check = TRUE; | |
} | |
// Determine if we should password protect the site. | |
// | |
// Current business rules: | |
// 1) Dev, stage, and prod are only accessible from whitelisted IPs. | |
// 2) Users on whitelisted IPs must pass basic auth challenge to access. | |
// | |
// Always start by assuming we're protecting the site if the relevant variables | |
// are set. Some rule below has to pass before we turn it off. | |
$protect_ip = !empty($conf['ah_whitelist']); | |
$protect_password = !empty($conf['ah_basic_auth_credentials']); | |
// Do not protect command line requests, e.g. Drush. | |
if ($cli) { | |
$protect_ip = FALSE; | |
$protect_password = FALSE; | |
} | |
// Un-comment to disable protection, e.g. for load tests. | |
// $skip_auth_check = TRUE; | |
// $on_vpn = TRUE; | |
// If not on whitelisted IP prevent all access. | |
if ($protect_ip && !$on_vpn) { | |
ah_page_403(); | |
} | |
// If not skipping auth, check basic auth. | |
if ($protect_password && !$skip_auth_check) { | |
ah_check_basic_auth(); | |
} | |
} | |
/** | |
* Output a 403 (forbidden access) response. | |
*/ | |
function ah_page_403() { | |
print "403 Forbidden: Access denied (". $_SERVER['AH_Client_IP'] .")"; | |
header('HTTP/1.0 403 Forbidden'); | |
exit; | |
} | |
/** | |
* Output a 401 (unauthorized) response. | |
*/ | |
function ah_page_401() { | |
header('WWW-Authenticate: Basic realm="This site is protected"'); | |
header('HTTP/1.0 401 Unauthorized'); | |
print "401 Unauthorized: Access denied (". $_SERVER['AH_Client_IP'] .")"; | |
exit; | |
} | |
/** | |
* Check basic auth against allowed values. | |
*/ | |
function ah_check_basic_auth() { | |
global $conf; | |
$authorized = FALSE; | |
$php_auth_user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : NULL; | |
$php_auth_pw = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : NULL; | |
$credentials = isset($conf['ah_basic_auth_credentials']) ? $conf['ah_basic_auth_credentials'] : NULL; | |
if ($php_auth_user && $php_auth_pw && !empty($credentials)) { | |
if (isset($credentials[$php_auth_user]) && $credentials[$php_auth_user] == $php_auth_pw) { | |
$authorized = TRUE; | |
} | |
} | |
if ($authorized) { | |
return; | |
} | |
// Always fall back to 401. | |
ah_page_401(); | |
} | |
/** | |
* Determine if the current path is in the list of paths to not cache. | |
*/ | |
function ah_path_no_cache() { | |
global $conf; | |
$q = isset($_GET['q']) ? $_GET['q'] : NULL; | |
$paths = isset($conf['ah_paths_no_cache']) ? $conf['ah_paths_no_cache'] : NULL; | |
if (!empty($q) && !empty($paths)) { | |
foreach ($paths as $path) { | |
if ($q == $path || strpos($q, $path) === 0) { | |
return TRUE; | |
} | |
} | |
} | |
} | |
/** | |
* Determine if the current path is in the list of paths on which to not check | |
* auth. | |
*/ | |
function ah_path_skip_auth() { | |
global $conf; | |
$q = isset($_GET['q']) ? $_GET['q'] : NULL; | |
$paths = isset($conf['ah_paths_skip_auth']) ? $conf['ah_paths_skip_auth'] : NULL; | |
if (!empty($q) && !empty($paths)) { | |
foreach ($paths as $path) { | |
if ($q == $path || strpos($q, $path) === 0) { | |
return TRUE; | |
} | |
} | |
} | |
} | |
/** | |
* Forbid access to user or admin paths if the user is not on an approved IP range. | |
* | |
* @todo Use a $conf variable for this. | |
*/ | |
function ah_paths_restrict($on_vpn) { | |
if (isset($_GET['q'])) { | |
$restricted_path = ($_GET['q'] == 'user' || $_GET['q'] == 'admin' || | |
strpos($_GET['q'], 'user/') === 0 || | |
strpos($_GET['q'], 'admin/') === 0); | |
// @TODO - remove debug | |
// header( 'X-AH-debug-restricted-path: ' . ($restricted_path ? "restricted" : "not restricted") ); | |
if ($restricted_path && !$on_vpn) { | |
ah_page_forbidden(); | |
} | |
} | |
} | |
/** | |
* Determine if the IP is within the ranges defined in the whitelist. | |
*/ | |
function ah_ip_in_whitelist($ip, $whitelist) { | |
foreach ($whitelist as $cidr) { | |
$range = ah_ip_cidr_range($cidr); | |
if ($range && count($range) == 2) { | |
$ip_long = ip2long($ip); | |
$ip_one_long = $range[0]; | |
$ip_two_long = $range[1]; | |
$lte = $ip_one_long <= $ip_long; | |
$gte = $ip_two_long >= $ip_long; | |
if ($lte && $gte) { | |
return TRUE; | |
} | |
} | |
} | |
} | |
/** | |
* From http://www.php.net/manual/en/function.ip2long.php#109298 | |
*/ | |
function ah_ip_cidr_range($cidr) { | |
list($ip, $mask) = explode('/', $cidr); | |
// net mask binary string | |
$maskBinStr = str_repeat("1", $mask ) . str_repeat("0", 32-$mask ); | |
// inverse mask | |
$inverseMaskBinStr = str_repeat("0", $mask ) . str_repeat("1", 32-$mask ); | |
$ipLong = ip2long($ip); | |
$ipMaskLong = bindec($maskBinStr); | |
$inverseIpMaskLong = bindec($inverseMaskBinStr); | |
$netWork = $ipLong & $ipMaskLong; | |
// ignore network ID (eg: 192.168.1.0) | |
$start = $netWork + 1; | |
// ignore brocast IP(eg: 192.168.1.255) | |
$end = ($netWork | $inverseIpMaskLong) - 1; | |
return array($start, $end); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sample code for settings.php includes and $conf settings that help you quickly
lock down an Acquia Cloud environment using basic auth and / or IP whitelisting.
$conf['ah_basic_auth_credentials']
An array of basic auth username /password combinations
$conf['ah_whitelist']
An array of IP addresses to allow on to the site.$conf['ah_blacklist']
An array of IP addresses that will be denied access to the site.$conf['ah_paths_no_cache']
Paths we should explicitly never cache.$conf['ah_paths_skip_auth']
Skip basic authentication for these paths.$conf['ah_restricted_paths']
Paths which may not be accessed unless the user is on the IP whitelist.ac_protect_this_site();
with defined $conf elements.Business Logic
ac_protect_this_site();
will do nothing.$conf['ah_basic_auth_credentials']
will result in all requests being requring an .htaccess log in.$conf['ah_whitelist']
and$conf['ah_restricted_paths']
Examples
Block access to non-whitelisted users on all pages of non-production environments.