Acquia
AcsfDeployWorkspaceCleaned
Ensure the ACSF deploy workspace is clear. This can bloat ephemeral storage.
Class: Drutiny\Acquia\Audit\AcsfDeployWorkspaceCleaned
Extends: Drutiny\Audit
Package: drutiny/acquia
This class can remediate failed audits.
Policies
These are the policies that use this class:
Name | Title |
---|---|
Acquia:CheckSiteFactoryWorkspace | ACSF Deployment Workspace |
Source
public function audit(Sandbox $sandbox) {
$output = $sandbox->exec('find /mnt/tmp/repo_* -maxdepth 1 -mindepth 1 -ctime +1 -type d');
$lines = array_filter(explode(PHP_EOL, $output));
$sandbox->setParameter('directories', $lines);
return empty($lines);
}
AppInfo
Ensure an environment has custom domains set.
Class: Drutiny\Acquia\Audit\AppInfo
Extends: Drutiny\Audit
Package: drutiny/acquia
Policies
These are the policies that use this class:
Name | Title |
---|---|
Acquia:AppInfo | Acquia Application Information |
Source
public function audit(Sandbox $sandbox) {
$target = $sandbox->getTarget();
if ($target instanceof AcquiaTargetInterface) {
$env = $target->getEnvironment();
$app = CloudApiV2::get('applications/' . $env['application']['uuid']);
}
else {
$options = $target->getOptions();
$app = CloudApiDrushAdaptor::findApplication($options['ac-realm'], $options['ac-site']);
}
$sandbox->setParameter('app', $app);
return Audit::NOTICE;
}
CustomDomains
Ensure an environment has custom domains set.
Class: Drutiny\Acquia\Audit\CustomDomains
Extends: Drutiny\Audit
Package: drutiny/acquia
Policies
These are the policies that use this class:
Name | Title |
---|---|
Acquia:CustomDomains | Custom Domains Registered |
Source
public function audit(Sandbox $sandbox) {
$environment = CloudApiDrushAdaptor::getEnvironment($sandbox->getTarget());
$domains = array_filter($environment['domains'], function ($domain) {
// Do not include ELB domains or Acquia default domains.
return !(strpos($domain, 'acquia-sites.com') || strpos($domain, 'elb.amazonaws.com') || strpos($domain, 'acsitefactory.com'));
});
if (empty($domains)) {
return FALSE;
}
$sandbox->setParameter('domains', array_values($domains));
return TRUE;
}
EnvironmentAnalysis
Check to ensure Production Mode is enabled on Acquia Cloud.
Class: Drutiny\Acquia\Audit\EnvironmentAnalysis
Extends: Drutiny\Audit\AbstractAnalysis
Package: drutiny/acquia
Policies
These are the policies that use this class:
Name | Title |
---|---|
Acquia:ProductionMode | Acquia Production Mode |
Source
public function gather(Sandbox $sandbox) {
$environment = CloudApiDrushAdaptor::getEnvironment($sandbox->getTarget());
$app = CloudApiV2::get('applications/' . $environment['application']['uuid']);
$sandbox->setParameter('environment', $environment);
$sandbox->setParameter('app', $app);
}
final public function audit(Sandbox $sandbox)
{
$this->gather($sandbox);
$expressionLanguage = new ExpressionLanguage($sandbox);
$variables = $sandbox->getParameterTokens();
$sandbox->logger()->debug(__CLASS__ . ': ' . Yaml::dump($variables));
$expression = $sandbox->getParameter('not_applicable', 'false');
$sandbox->logger()->debug(__CLASS__ . ': ' . $expression);
if (@$expressionLanguage->evaluate($expression, $variables)) {
return self::NOT_APPLICABLE;
}
$expression = $sandbox->getParameter('expression', 'true');
$sandbox->logger()->info(__CLASS__ . ': ' . $expression);
return @$expressionLanguage->evaluate($expression, $variables);
}
FilesPerDirectory
Class: Drutiny\Acquia\Audit\FilesPerDirectory
Extends: Drutiny\Audit
Package: drutiny/acquia
Policies
These are the policies that use this class:
Name | Title |
---|---|
Acquia:FilesPerDirectory | Less than 2,500 files per directory |
Parameters
Name | Type | Description | Default |
---|---|---|---|
limit | integer | The limit of files per directory. | 2500 |
Tokens
Name | Type | Description | Default |
---|---|---|---|
limit | integer | The limit of files per directory. | 2500 |
directories | array | A multidimensional array containing directory data breaching the limit. | { } |
Source
public function audit(Sandbox $sandbox) {
// Build a count of files in every directory in the public filesystem.
$stat = $sandbox->drush(['format' => 'json'])->status();
$files = implode('/', [$stat['root'], $stat['files']]);
$limit = $sandbox->getParameter('limit');
// Note the trailing slash at the end of $files to ensure find works over
// symlinks.
$command = "find $files/ -type f -exec dirname {} \; | uniq -c | awk '\$1 > $limit'";
// Execute and clean the output into usable data.
$output = $sandbox->exec($command);
$lines = array_filter(explode(PHP_EOL, $output));
$directories = [];
foreach ($lines as $line) {
list($count, $directory) = explode(' ', trim($line));
$directories[] = [
'directory' => str_replace($stat['root'] . '/', '', $directory),
'file_count' => $count,
];
}
// No limits breached, return as pass.
if (empty($directories)) {
return TRUE;
}
$sandbox->setParameter('directories', $directories);
return FALSE;
}
SearchDB
Ensure there is no use of search indexes in the database.
Class: Drutiny\Acquia\Audit\SearchDB
Extends: Drutiny\Plugin\Drupal7\Audit\ModuleDisabled
Package: drutiny/acquia
This class can remediate failed audits.
Policies
These are the policies that use this class:
Name | Title |
---|---|
Acquia:Drupal7:NoDbSearch | No Database Search Indexes for Drupal 7 |
Parameters
Name | Type | Description | Default |
---|---|---|---|
module | string | The module to check is enabled. | search_api_db |
Tokens
Name | Type | Description | Default |
---|---|---|---|
module | string | The module to check is enabled. | search_api_db |
indexes | array | An array of index names found in the database. Only available on failure. | null |
items | integer | The number of indexed items in the database. Only available on failure. | null |
Source
public function audit(Sandbox $sandbox) {
$sandbox->setParameter('module', 'search_api_db');
if (parent::audit($sandbox)) {
return TRUE;
}
// Find out if there are active indexes using the db service class.
$output = $sandbox->drush()->sqlq("SELECT COUNT(i.machine_name) as count FROM {search_api_index} i LEFT JOIN {search_api_server} s ON i.server = s.machine_name WHERE i.status > 0 AND s.class = 'search_api_db_service';");
if (empty($output)) {
return TRUE;
}
elseif (count($output) == 1) {
$number_of_db_indexes = (int) $output[0];
}
else {
$number_of_db_indexes = (int) $output[1];
}
$sandbox->setParameter('indexes', $number_of_db_indexes);
$output = $sandbox->drush()->sqlq('SELECT COUNT(item_id) FROM {search_api_db_default_node_index};');
$sandbox->setParameter('items', $output);
return FALSE;
}
SecureDomains
Ensure an environment has custom domains set.
Class: Drutiny\Acquia\Audit\SecureDomains
Extends: Drutiny\Audit
Package: drutiny/acquia
Policies
These are the policies that use this class:
Name | Title |
---|---|
Acquia:SecureDomains | Secured Domains |
Source
public function audit(Sandbox $sandbox) {
$environment = CloudApiDrushAdaptor::getEnvironment($sandbox->getTarget());
// Check each IP associated with the "front" of the environment.
$ssl_domains = $environment['ips'];
// Default domain will point at either the Acquia balancers or the ELB.
$ssl_domains[] = $environment['default_domain'];
// Build a list of domains that "should" be secured by SSL certed hosted in
// $ssl_domains with the exception of domains hosted at the edge.
$domains = array_filter($environment['domains'], function ($domain) {
// Do not include ELB domains or Acquia default domains.
return !(strpos($domain, 'acquia-sites.com') || strpos($domain, 'elb.amazonaws.com') || strpos($domain, 'acsitefactory.com'));
});
$domain_details = [];
foreach ($domains as $domain) {
$domain_details[$domain] = [
'name' => $domain,
'secure' => FALSE,
'certificate' => FALSE,
'host' => FALSE,
];
}
$checked_certs = [];
foreach ($ssl_domains as $host) {
foreach ($domains as $sni_domain) {
// No need to try any further certs for SNI domain if already confirmed
// as secure.
if ($domain_details[$sni_domain]['secure']) {
continue;
}
try {
$cert = $this->getCertificate($host, $sni_domain);
}
catch (\Exception $e) {
$sandbox->logger()->warning($e->getMessage());
continue;
}
// No need to check the same cert if we've picked it up from somewhere else.
if (in_array($cert['extensions']['subjectKeyIdentifier'], $checked_certs)) {
continue;
}
$checked_certs[] = $cert['extensions']['subjectKeyIdentifier'];
// SANs are the only places where we can check for the domain.
if (!isset($cert['extensions']['subjectAltName'])) {
continue;
}
$san = $cert['extensions']['subjectAltName'];
$secured_domains = array_filter($domains, function ($domain) use ($san) {
return preg_match("/DNS:$domain(,|$)/", $san);
});
foreach ($secured_domains as $domain) {
$domain_details[$domain]['secure'] = TRUE;
$domain_details[$domain]['certificate'] = strtr('CN - O', $cert['subject']);
$domain_details[$domain]['host'] = $host;
}
}
}
$insecured = array_filter($domain_details, function ($domain) {
return !$domain['secure'];
});
$sandbox->setParameter('domains', array_values($domain_details));
if (count($insecured) == count($domain_details)) {
return FALSE;
}
return count($insecured) ? Audit::WARNING : TRUE;
}
StackMetrics
Ensure an environment has custom domains set.
Class: Drutiny\Acquia\Audit\StackMetrics
Extends: Drutiny\Audit\AbstractAnalysis
Package: drutiny/acquia
Parameters
Name | Type | Description | Default |
---|---|---|---|
metrics | array | one of apache-requests, bal-cpu, bal-memory, cron-memory, db-cpu, db-disk-size, db-disk-usage, db-memory, file-disk-size, file-cpu, file-disk-usage, file-memory, http-2xx, http-3xx, http-4xx, http-5xx, mysql-slow-query-count, nginx-requests, out-of-memory, php-proc-max-reached-site, php-proc-max-reached-total, php-proc-site, php-proc-total, varnish-cache-hit-rate, varnish-requests, web-cpu, web-memory | - web-cpu - web-memory |
Tokens
Name | Type | Description | Default |
---|---|---|---|
metrics | array | one of apache-requests, bal-cpu, bal-memory, cron-memory, db-cpu, db-disk-size, db-disk-usage, db-memory, file-disk-size, file-cpu, file-disk-usage, file-memory, http-2xx, http-3xx, http-4xx, http-5xx, mysql-slow-query-count, nginx-requests, out-of-memory, php-proc-max-reached-site, php-proc-max-reached-total, php-proc-site, php-proc-total, varnish-cache-hit-rate, varnish-requests, web-cpu, web-memory | - web-cpu - web-memory |
Source
public function gather(Sandbox $sandbox) {
$target = $sandbox->getTarget();
$env = ($target instanceof AcquiaTargetInterface) ? $target->getEnvironment() : CloudApiDrushAdaptor::getEnvironment($target);
$metrics = $sandbox->getParameter('metrics');
if (!is_array($metrics)) {
throw new AuditValidationException("Metrics parameter must be an array. " . ucwords(gettype($metrics)) . ' given.');
}
$response = CloudApiV2::get('environments/' . $env['id'] . '/metrics/stackmetrics/data', [
'filter' => implode(',', array_map(function ($metric) {
return 'metric:' . $metric;
}, $metrics)),
'from' => $sandbox->getReportingPeriodStart()->format(\DateTime::ISO8601),
'to' => $sandbox->getReportingPeriodEnd()->format(\DateTime::ISO8601),
]);
$table_headers = ['Date'];
$table_rows = [];
foreach ($response['_embedded']['items'] as $item) {
if (!empty($item['host'])) {
list($item['name'],) = explode('.', $item['host'], 2);
}
if (!isset($item['name'])) {
$item['name'] = $item['metric'];
}
elseif (count($metrics) > 1) {
$item['name'] .= ':' . $item['metric'];
}
$table_headers[] = $item['name'];
$idx = array_search($item['name'], $table_headers);
foreach ($item['datapoints'] as $plot) {
// $y == value
// $x == epoch
list($y, $x) = $plot;
// Convert unix timestamp plot point to readable datetime.
if (!isset($table_rows[$x])) {
$table_rows[$x] = [ date('Y-m-d H:i:s', $x) ];
}
$table_rows[$x][$idx] = $y;
}
}
// Sort the table columns by index.
array_walk($table_rows, 'ksort');
$sandbox->setParameter('result', $response);
$sandbox->setParameter('env', $env);
$sandbox->setParameter('table_headers', $table_headers);
$sandbox->setParameter('table_rows', array_values($table_rows));
// graph
$graph = [
'type' => 'line',
'labels' => 'tr td:first-child',
'hide-table' => TRUE,
'height' => 250,
'width' => 400,
'stacked' => FALSE,
'title' => $sandbox->getPolicy()->get('title'),
'y-axis' => 'Percentage',
'series' => [],
'series-labels' => [],
'legend' => 'bottom',
];
foreach ($table_headers as $idx => $name) {
if ($name == 'Date') {
continue;
}
$nth = $idx + 1;
$graph['series'][] = 'tr td:nth-child(' . $nth . ')';
$graph['series-labels'][] = 'tr th:nth-child(' . $nth . ')';
}
$graph['series'] = implode(',', $graph['series']);
$graph['series-labels'] = implode(',', $graph['series-labels']);
$element = [];
foreach ($graph as $key => $value) {
$element[] = $key . '="' . $value . '"';
}
$element = '[[[' . implode(' ', $element) . ']]]';
$sandbox->setParameter('graph', $element);
}
final public function audit(Sandbox $sandbox)
{
$this->gather($sandbox);
$expressionLanguage = new ExpressionLanguage($sandbox);
$variables = $sandbox->getParameterTokens();
$sandbox->logger()->debug(__CLASS__ . ': ' . Yaml::dump($variables));
$expression = $sandbox->getParameter('not_applicable', 'false');
$sandbox->logger()->debug(__CLASS__ . ': ' . $expression);
if (@$expressionLanguage->evaluate($expression, $variables)) {
return self::NOT_APPLICABLE;
}
$expression = $sandbox->getParameter('expression', 'true');
$sandbox->logger()->info(__CLASS__ . ': ' . $expression);
return @$expressionLanguage->evaluate($expression, $variables);
}