diff --git a/Policies/cnd_check.policy.yml b/Policies/cdn_check.policy.yml similarity index 100% rename from Policies/cnd_check.policy.yml rename to Policies/cdn_check.policy.yml diff --git a/Policies/email_checker.policy.yml b/Policies/email_checker.policy.yml new file mode 100644 index 0000000..fc444c1 --- /dev/null +++ b/Policies/email_checker.policy.yml @@ -0,0 +1,17 @@ +title: "Email checker" +class: \Drutiny\algm\Audit\EmailChecker +name: algm:EmailChecker +tags: + - Email + - Routing +description: | + A policy check that emails are not sending on non-prod environments. +success: | + {{ status }} +failure: | + {{ status }} +warning: | + {{ warning_message }} +parameters: + module_to_check: + default: reroute_email diff --git a/README.md b/README.md index dbc9ea8..02c4425 100644 --- a/README.md +++ b/README.md @@ -5,39 +5,40 @@ This plugin provides a list of standard policy collections that can be used by D ## Setup - 1. `git clone git@github.com:AmazeeLabs/algm_drutiny.git` +1. `git clone git@github.com:AmazeeLabs/algm_drutiny.git` - 2. `composer install -o` +1. `composer install -o` - 3. Need Drush - https://docs.drush.org/en/9.x/install/ +1. Need Drush - https://docs.drush.org/en/9.x/install/ - We also need Drush locally (preferably Drush 9+). There are also a couple of things you need to ensure your drush alias files have in order to get this working with Drutiny which we will cover below. + > We also need Drush locally (preferably Drush 9+). There are also a couple of things you need to ensure your drush alias files have in order to get this working with Drutiny which we will cover below. + > + > If you have drush but need to update to Drush 9 way of things, then this is your friend: + > https://stackoverflow.com/questions/55587919/where-drush-9-aliases-file-should-be-located-in-drupal-8 + > + > To install globally in terminal (But aware that this will affect previous Drush setup) + > In your user folder. + > + > $ `composer global require drush/drush:^9` - If you have drush but need to update to Drush 9 way of things, then this is your friend: - https://stackoverflow.com/questions/55587919/where-drush-9-aliases-file-should-be-located-in-drupal-8 - - To install globally in terminal (But aware that this will affect previous Drush setup) - In your user folder. - - $ `composer global require drush/drush:^9` - - 4. Drush Setup folder structure in ~/.drush/ +1. Drush Setup folder structure in ~/.drush/ + ``` ~/.drush/ - cache/ (might not be there) - commands/ (might not be there) - sites/ ( Add this Drush Alias folder. This is where you add your sitename.site.yml for each sites you'll connect to.) - drush.yml - + - drush.yml should have: - + drush: paths: alias-path: - '${env.home}/.drush/sites' + ``` - - 5. Test Drutiny is running - `./vendor/bin/drutiny` +1. Test Drutiny is running - `./vendor/bin/drutiny` ## What is this? @@ -131,18 +132,30 @@ For more config options - this is useful https://github.com/drush-ops/drush/blob ## Useful things -Checking policy list available to us: - `./vendor/bin/drutiny policy:list` +**Checking policy list available** +``` +./vendor/bin/drutiny policy:list +``` + +Show only `algm`: +``` +./vendor/bin/drutiny policy:list | grep algm +``` -Checking profiles list: - `./vendor/bin/drutiny profile:list` +**Checking profiles list available** +``` +./vendor/bin/drutiny profile:list +``` -You might need to clear Drutiny cache: - `./vendor/bin/drutiny cache:clear` +**Clearing Drutiny cache** +``` +./vendor/bin/drutiny cache:clear +``` ## Development and testing Inside the package there is a Drupal installation where you can test -your policies against. Please follow the -[drupal-web/README.md](drupal-web/README.md) file +your policies against. + +Please follow the [drupal-web/README.md](drupal-web/README.md) file diff --git a/drupal-web/cli.dockerfile b/drupal-web/cli.dockerfile index bc8e558..0283aee 100644 --- a/drupal-web/cli.dockerfile +++ b/drupal-web/cli.dockerfile @@ -1,6 +1,6 @@ FROM algmprivsecops/algmcli:latest as algmtools -FROM amazeeio/php:7.2-cli-drupal +FROM amazeeio/php:7.4-cli-drupal COPY composer.json composer.lock /app/ COPY scripts /app/scripts @@ -12,4 +12,4 @@ COPY . /app COPY --from=algmtools /ALGM /ALGM # Define where the Drupal Root is located -ENV WEBROOT=web \ No newline at end of file +ENV WEBROOT=web diff --git a/drupal-web/php.dockerfile b/drupal-web/php.dockerfile index 0c91f03..d3e1487 100644 --- a/drupal-web/php.dockerfile +++ b/drupal-web/php.dockerfile @@ -1,6 +1,6 @@ ARG CLI_IMAGE FROM ${CLI_IMAGE} as cli -FROM amazeeio/php:7.2-fpm +FROM amazeeio/php:7.4-fpm COPY --from=cli /app /app diff --git a/src/Audit/EmailChecker.php b/src/Audit/EmailChecker.php new file mode 100644 index 0000000..2a6b462 --- /dev/null +++ b/src/Audit/EmailChecker.php @@ -0,0 +1,240 @@ +getTarget() instanceof DrushTarget; + } + + /** + * @inheritdoc + */ + public function audit(Sandbox $sandbox) { + + // Not in actual use for now + $module_to_check = $sandbox->getParameter('module_to_check'); + + $status = $sandbox->drush(['format' => 'json'])->status(); + if (!$status) { + return Audit::ERROR; + } + + // First check that we are in a dev env + $command = "env"; + $output = $sandbox->exec($command); + + if(!$output) { + return Audit::ERROR; + } + + $lines = array_filter(explode(PHP_EOL, $output)); + $lagoon_env_type =''; + foreach ($lines as $line) { + if(strpos($line, 'LAGOON_ENVIRONMENT_TYPE') === 0) { + $info = explode('=',$line); + $lagoon_env_type = $info[1]; + } + } + // print $lagoon_env_type . PHP_EOL; + if(!$lagoon_env_type) { + $msg = 'Cannot determinate the environment.' . PHP_EOL; + $sandbox->setParameter('warning_message', $msg); + return Audit::WARNING; + } + + if ($lagoon_env_type === 'production') { + $msg = 'This policy can only run in a non-production environment.' . PHP_EOL; + $sandbox->setParameter('warning_message', $msg); + return Audit::WARNING; + } + + // Let's start module by module + // because modules have different settings + $info = $sandbox->drush(['format' => 'json'])->pmList(); + + // Reroute_email + if (isset($info['reroute_email']) && strtolower($info['reroute_email']['status']) === 'enabled') { + // do something + $cmd = "drush cget reroute_email.settings --include-overridden --format=json"; + $settings = json_decode($sandbox->exec($cmd), TRUE); + if($settings['enable']) { + $address = $settings['address']; + + if(!filter_var($address, FILTER_VALIDATE_EMAIL)){ + $msg = 'Reroute email is enabled but is not using a valid email address' . PHP_EOL; + $sandbox->setParameter('warning_message', $msg); + return Audit::WARNING; + } + + $status_output = 'Reroute email is enabled.' . PHP_EOL; + $status_output .= 'All emails are redirected to: ' . $address . PHP_EOL; + $sandbox->setParameter('status', trim($status_output)); + return Audit::SUCCESS; + } + } + + // SMTP + if (isset($info['smtp']) && strtolower($info['smtp']['status']) === 'enabled') { + // do something + $cmd = "drush cget smtp.settings --include-overridden --format=json"; + $settings = json_decode($sandbox->exec($cmd), TRUE); + + $enabled = $settings['smtp_on']; + if($enabled) { + + $smtp_host = $settings['smtp_host']; + $search_for = 'mailhog'; + + if (preg_match("/{$search_for}/i", $smtp_host)) { + $status_output = 'SMTP host is configured to use: ' . $smtp_host . PHP_EOL; + $sandbox->setParameter('status', trim($status_output)); + return Audit::SUCCESS; + } + } + + } + + // Swiftmailer + // This is a quick and dirty solution + if (isset($info['swiftmailer']) && strtolower($info['swiftmailer']['status']) === 'enabled') { + // do something + $cmd = "drush cget swiftmailer.transport --include-overridden --format=json"; + $settings = json_decode($sandbox->exec($cmd), TRUE); + $smtp_host = $settings['smtp_host']; + $search_for = 'mailhog'; + if(preg_match("/{$search_for}/i", $smtp_host)) { + $status_output = 'SMTP host is configured to use: ' . $smtp_host . PHP_EOL; + $sandbox->setParameter('status', trim($status_output)); + return Audit::SUCCESS; + } + } + + // Let's try to search in settings + $settings_path = $status['root'] . "/sites/default"; + $cmd = 'find ' . $settings_path . ' -name "*.php" | xargs grep -i "smtp\|mail"'; + $results = $sandbox->exec($cmd); + $files = explode(PHP_EOL, $results); + foreach ($files as $file) { + $search_for = 'development'; + $is_development_setting_file = preg_match("/{$search_for}/i", $file); + $search_for = "\[\'smtp_host\'\] = \'mailhog"; + $is_using_mailhog = preg_match("/{$search_for}/i", $file); + if ($is_development_setting_file && $is_using_mailhog) { + $status_output = 'SMTP host is configured to use mailhog.' . PHP_EOL; + $sandbox->setParameter('status', trim($status_output)); + return Audit::SUCCESS; + } + } + + // Idea: if you find instances of smtp_host in prod and dev php settings + // then compare them + $dev_smtp_host = ''; + $prod_smtp_host = ''; + $dev_reroute_email = ''; + foreach ($files as $file) { + $config_line = substr($file, strpos($file, ":") + 1); + $search_for = 'development'; + $is_development_setting_file = preg_match("/{$search_for}/i", $config_line); + $search_for = 'production'; + $is_production_setting_file = preg_match("/{$search_for}/i", $config_line); + $search_for = "\[\'smtp_host\'\]"; + $contains_smtp_host = preg_match("/{$search_for}/i", $config_line); + $search_for = "reroute_email"; + $has_reroute_in_settings = preg_match("/{$search_for}/i", $config_line); + + $comments = '//'; + $line_commented_out = substr($config_line, 0, strlen($comments)) === $comments; + + $split = "['smtp_host'] ="; + if ($is_development_setting_file && $contains_smtp_host) { + $smtp_host = explode($split,$file); + $smtp_host = preg_replace("/\'|\;/", "" , $smtp_host[0]); + $dev_smtp_host = [ + 'details' => $smtp_host, + 'commented_out' => $line_commented_out + ]; + } + if ($is_production_setting_file && $contains_smtp_host) { + $smtp_host = explode($split,$file); + $smtp_host = preg_replace("/\'|\;/", "" , $smtp_host[0]); + $prod_smtp_host = [ + 'details' => $smtp_host, + 'commented_out' => $line_commented_out + ]; + } + if ($is_development_setting_file && $has_reroute_in_settings) { + $email = explode(" = ",$file); + $email = preg_replace("/\'|\;/", "" , $email[1]); + $dev_reroute_email = [ + 'details' => $email, + 'commented_out' => $line_commented_out + ]; + } + } + + // Another case is not to have reroute_email but have something like + // config['amag_processes.location_settings']['reroute_email_address'] = 'development+amag@amazee.com'; + // so let's try to detect something like that + if (!empty($dev_reroute_email) && $dev_reroute_email['details'] && !$dev_reroute_email['commented_out']) { + $status_output = "All emails are redirected to " . $dev_reroute_email['details'] . PHP_EOL; + $sandbox->setParameter('status', trim($status_output)); + return Audit::SUCCESS; + } + + // Check to see if the smtp details not the same for prod and dev + if (!empty($prod_smtp_host) && !empty($dev_smtp_host) + && $prod_smtp_host['details'] != $dev_smtp_host['details'] + && !$prod_smtp_host['commented_out'] && !$dev_smtp_host['commented_out']) { + $status_output = "SMTP host is different on prod and dev environments." . PHP_EOL; + $status_output .= "SMTP host in production:\t" . $prod_smtp_host . PHP_EOL; + $status_output .= "SMTP host in environment:\t" . $dev_smtp_host . PHP_EOL; + $status_output .= "Notice: You may want to check further these settings." . PHP_EOL; + $sandbox->setParameter('status', trim($status_output)); + return Audit::SUCCESS; + } + + // TODO: We definitely need more cases/scenarios to inspect + // the above implementation is just a basic check + // and is oriented to AmazeeIO environments + // e.g. + // 1. Check config folder for reroute_email records + + $status_output = 'Could not determinate the status of the SMTP host in the environment.' . PHP_EOL; + $sandbox->setParameter('status', trim($status_output)); + return Audit::FAIL; + } + +} //end class