You are here

class Provision_Service_Certificate_LetsEncrypt in Aegir HTTPS 7.3

A LetsEncrypt implementation of the Certificate service type.

Hierarchy

Expanded class hierarchy of Provision_Service_Certificate_LetsEncrypt

File

submodules/letsencrypt/drush/Provision/Service/Certificate/LetsEncrypt.php, line 6

View source
class Provision_Service_Certificate_LetsEncrypt extends Provision_Service_Certificate {
  public $service = 'LetsEncrypt';

  /**
   * Initialize this class, including option handling.
   */
  function init_server() {
    parent::init_server();

    /**
     * Register configuration classes for the create_config / delete_config methods.
     */
    $this->configs['server'][] = 'Provision_Config_LetsEncrypt';

    /**
     * Configurable values.
     */
    $this->server
      ->setProperty('letsencrypt_ca', 'staging');

    /**
     * Non configurable values.
     */
    $this->server->letsencrypt_script_path = $this->server->aegir_root . '/config/letsencrypt';
    $this->server->letsencrypt_config_path = $this->server->aegir_root . '/config/letsencrypt.d';
    $this->server->letsencrypt_challenge_path = $this->server->aegir_root . '/config/letsencrypt.d/well-known/acme-challenge';
  }

  /**
   * Pass additional values to the config file templates.
   *
   * Even though the $server variable will be available in your template files,
   * you may wish to pass additional calculated values to your template files.
   *
   * Consider this something like the hook_preprocess stuff in drupal.
   */
  function config_data($config = null, $class = null) {

    // This format of calling the parent is very important!
    $data = parent::config_data($config, $class);

    /**
     * This value will become available as $letsencrypt_current_time
     * in all the config files generated by this service.
     *
     * You could also choose to only conditionally pass values based on
     * the parameters.
     */
    $data['letsencrypt_current_time'] = date(DATE_COOKIE, time());
    return $data;
  }

  /**
   * Return the path where we'll generate our certificates.
   */
  function get_source_path($https_key) {
    return "{$this->server->letsencrypt_config_path}/{$https_key}";
  }

  /**
   * Retrieve an array containing the actual files for this https_key.
   */
  function get_certificates($https_key) {
    $certs = parent::get_certificates($https_key);

    // This method is not strictly required, since it's just calling the parent
    // implementation. However, for illustrative purposes, this is where we'd
    // alter certificate paths, if we wanted to.
    return $certs;
  }

  /**
   * Retrieve an array containing source and target paths for this https_key.
   */
  function get_certificate_paths($https_key) {
    $source_path = $this
      ->get_source_path($https_key);
    $target_path = "{$this->server->http_ssld_path}/{$https_key}";
    $certs = array();
    $certs['https_cert_key_source'] = "{$source_path}/privkey.pem";
    $certs['https_cert_key'] = "{$target_path}/openssl.key";
    $certs['https_cert_source'] = "{$source_path}/fullchain.pem";
    $certs['https_cert'] = "{$target_path}/openssl.crt";
    return $certs;
  }

  /**
   * Generate a self-signed certificate for the provided key.
   *
   * Because we only generate certificates for sites we make some assumptions
   * based on the uri, but this cert may be replaced by the admin if they
   * already have an existing certificate.
   */
  function generate_certificates($https_key) {
    $path = $this
      ->get_source_path($https_key);
    provision_file()
      ->create_dir($path, dt("HTTPS certificate directory for %https_key", array(
      '%https_key' => $https_key,
    )), 0700);
    $config_file = $this
      ->getConfigFile($this->server->letsencrypt_ca);
    $script_path = $this->server->letsencrypt_script_path;
    $config_path = $this->server->letsencrypt_config_path;
    $drush_alias = escapeshellarg('@' . d()->uri);
    $domain_list = $this
      ->getDomainsString(d());
    $on_remote_server = !provision_is_local_host(d()->platform->web_server->remote_host);
    $le_hook = $script_path . '/dehydrated-hooks.sh';
    $le_options = '--cron --accept-terms';
    if ($on_remote_server) {
      $le_options .= ' --hook ' . $le_hook;
    }
    drush_log(dt("Generating Let's Encrypt certificates."));
    $cmd = "AEGIR_DRUSH_ALIAS={$drush_alias} {$script_path}/script {$le_options} --config {$script_path}/{$config_file} --out {$config_path} {$domain_list}";
    drush_log("Running: " . $cmd, 'notice');
    $result = drush_shell_exec($cmd);
    if ($result) {
      foreach (drush_shell_exec_output() as $line) {
        drush_log($line);
      }
      drush_log(dt("Successfully generated Let's Encrypt certificates."), 'success');
    }
    else {
      foreach (drush_shell_exec_output() as $line) {
        drush_log($line, 'warning');
      }
      if (drush_get_option('hosting_https_fail_task_if_certificate_fails', FALSE)) {
        drush_set_error('HTTPS_CERT_GEN_FAIL', dt("Failed to generate Let's Encrypt certificates."));
      }
      else {
        drush_log(dt("Failed to generate Let's Encrypt certificates."), 'warning');
      }
    }
  }

  /**
   * Fetches the configuration file for specified environment.
   *
   * @param string $environment
   *   Either 'staging' or 'production'.
   *
   * @todo: If we ever need more granular control, we can generate the config
   *   file instead.
   */
  protected function getConfigFile($environment) {
    if ($environment == 'production') {
      return 'config';
    }
    return 'config.staging';
  }

  /**
   * Returns a string specifying the site names we'd like on the certificate.
   *
   * An example would be "--domain example.com --domain www.example.com" where the former is
   * the canonical name, and the latter is one possible alternate name.
   */
  protected function getDomainsString($context) {
    $canonical_name = $context->uri;
    $options_list = array(
      "--domain {$canonical_name}",
    );
    if (isset($context->aliases)) {
      foreach ($context->aliases as $alias) {
        if (!in_array("--domain {$alias}", $options_list)) {
          $options_list[] = "--domain {$alias}";
        }
      }
    }
    return implode(" ", $options_list);
  }

  /**
   * Implementation of service verify.
   *
   * Called from drush_certificate_provision_verify().
   */
  function verify() {
    parent::verify();
    if ($this->context->type == 'server') {
      $source = dirname(dirname(dirname(dirname(__FILE__)))) . '/bin';

      // Create the configuration file directory.
      provision_file()
        ->create_dir($this->server->letsencrypt_config_path, dt("Let's Encrypt configuration directory"), 0711);

      // Create the ACME challenge directory.
      provision_file()
        ->create_dir($this->server->letsencrypt_challenge_path, dt("Let's Encrypt ACME challenge directory"), 0711);

      // Create the script directory.
      provision_file()
        ->create_dir($this->server->letsencrypt_script_path, dt("Let's Encrypt script + data directory"), 0711);

      // Initialize config.
      provision_file()
        ->copy($source . '/config', $this->server->letsencrypt_script_path . '/config');
      provision_file()
        ->copy($source . '/config.staging', $this->server->letsencrypt_script_path . '/config.staging');

      // Initialize hooks file.
      provision_file()
        ->copy($source . '/dehydrated-hooks.sh', $this->server->letsencrypt_script_path . '/dehydrated-hooks.sh');
      provision_file()
        ->chmod($this->server->letsencrypt_script_path . '/dehydrated-hooks.sh', 0755);
      if (drush_copy_dir($source . '/dehydrated', $this->server->letsencrypt_script_path . '/dehydrated', FILE_EXISTS_OVERWRITE)) {
        drush_log("Copied Let's Encrypt dehydrated script code into place.", 'success');
      }
      provision_file()
        ->chmod($this->server->letsencrypt_script_path . '/dehydrated/dehydrated', 0755);

      // Symlink the dehydrated code into place.
      provision_file()
        ->symlink($this->server->letsencrypt_script_path . '/dehydrated/dehydrated', $this->server->letsencrypt_script_path . '/script', dt("Create Let's Encrypt dehydrated symlink."), 0644);

      // Sync the directory to the remote server if needed.

      #  $this->sync($this->server->letsencrypt_config_path);
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
Provision_Service_Certificate::$can_generate_default public property 1
Provision_Service_Certificate::create_platform_config function Add a new platform specific configuration file.
Provision_Service_Certificate::create_server_config function Create a new server specific configuration file.
Provision_Service_Certificate::create_site_config function Generate a site specific configuration file.
Provision_Service_Certificate::delete_platform_config function Remove an existing platform configuration file.
Provision_Service_Certificate::delete_server_config function Remove an existing server specific configuration file.
Provision_Service_Certificate::delete_site_config function Remove an existing site configuration file.
Provision_Service_Certificate::parse_configs function Commonly something like running the restart_cmd or sending SIGHUP to a process.
Provision_Service_Certificate_LetsEncrypt::$service public property Overrides Provision_Service_Certificate::$service
Provision_Service_Certificate_LetsEncrypt::config_data function Pass additional values to the config file templates.
Provision_Service_Certificate_LetsEncrypt::generate_certificates function Generate a self-signed certificate for the provided key. Overrides Provision_Service_Certificate::generate_certificates
Provision_Service_Certificate_LetsEncrypt::getConfigFile protected function Fetches the configuration file for specified environment.
Provision_Service_Certificate_LetsEncrypt::getDomainsString protected function Returns a string specifying the site names we'd like on the certificate.
Provision_Service_Certificate_LetsEncrypt::get_certificates function Retrieve an array containing the actual files for this https_key. Overrides Provision_Service_Certificate::get_certificates
Provision_Service_Certificate_LetsEncrypt::get_certificate_paths function Retrieve an array containing source and target paths for this https_key. Overrides Provision_Service_Certificate::get_certificate_paths
Provision_Service_Certificate_LetsEncrypt::get_source_path function Return the path where we'll generate our certificates. Overrides Provision_Service_Certificate::get_source_path
Provision_Service_Certificate_LetsEncrypt::init_server function Initialize this class, including option handling.
Provision_Service_Certificate_LetsEncrypt::verify function Implementation of service verify. Overrides Provision_Service_Certificate::verify