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 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_list($_SERVER['AH_Client_IP'], $conf['ah_whitelist']); | |
} | |
// If the IP is not explicitly whitelisted check to see if the IP is blacklisted. | |
if (!$on_vpn && !empty($_SERVER['AH_Client_IP']) && !empty($conf['ah_blacklist'])) { | |
if (ah_ip_in_list($_SERVER['AH_Client_IP'], $conf['ah_blacklist'])) { | |
ah_page_403(); | |
} | |
} | |
// Should we skip the auth check? Default to FALSE. | |
$skip_auth_check = FALSE; | |
// Check if we should skip auth check for this page. | |
if (ah_path_skip_auth()) { | |
$skip_auth_check = TRUE; | |
} | |
// Check if we should disable cache for this page. | |
if (ah_path_no_cache()) { | |
$conf['page_cache_maximum_age'] = 0; | |
} | |
// Is the page restricted to whitelist only? Default to FALSE. | |
$restricted_page = FALSE; | |
// Check to see whether this page is restricted. | |
if (!empty($conf['ah_restricted_paths']) && ah_paths_restrict()) { | |
$restricted_page = TRUE; | |
} | |
$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 access to protected pages. | |
if ($protect_ip && !$on_vpn && $restricted_page) { | |
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() { | |
header('HTTP/1.0 403 Forbidden'); | |
print "403 Forbidden: Access denied (". $_SERVER['AH_Client_IP'] .")"; | |
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; | |
} | |
} | |
} | |
} | |
/** | |
* Check whether a path has been restricted. | |
* | |
*/ | |
function ah_paths_restrict() { | |
global $conf; | |
if (isset($_GET['q'])) { | |
// Borrow some code from drupal_match_path() | |
foreach ($conf['ah_restricted_paths'] as &$path) { | |
$path = preg_quote($path, '/'); | |
} | |
$paths = preg_replace('/\\\\\*/', '.*', $conf['ah_restricted_paths']); | |
$paths = '/^(' . join('|', $paths) . ')$/'; | |
// If this is a restricted path, return TRUE. | |
if (preg_match($paths, $_GET['q'])) { | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
/** | |
* Determine if the IP is within the ranges defined in the white/black list. | |
*/ | |
function ah_ip_in_list($ip, $list) { | |
foreach ($list as $item) { | |
// Match IPs in CIDR format. | |
if (strpos($item, '/') !== false) { | |
list($range, $mask) = explode('/', $item); | |
// Take the binary form of the IP and range. | |
$ip_dec = ip2long($ip); | |
$range_dec = ip2long($range); | |
// Create the binary form of netmask. | |
$mask_dec = ~ (pow(2, (32 - $mask)) - 1); | |
// Run a bitwise AND to determine whether the IP and range exist | |
// within the same netmask. | |
if (($mask_dec & $ip_dec) == ($mask_dec & $range_dec)) { | |
return TRUE; | |
} | |
} | |
// Match against wildcard IPs or IP ranges. | |
elseif (strpos($item, '*') !== false || strpos($item, '-') !== false) { | |
// Construct a range from wildcard IPs | |
if (strpos($item, '*') !== false) { | |
$item = str_replace('*', 0, $item) . '-' . str_replace('*', 255, $item); | |
} | |
// Match against ranges by converting to long IPs. | |
list($start, $end) = explode('-', $item); | |
if (ip2long($start) <= ip2long($ip) && ip2long($ip) <= ip2long($end)) { | |
return TRUE; | |
} | |
} | |
// Match against single IPs | |
elseif ($ip === $item) { | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} |
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.