ssl.php

  1. 7.x-3.x http/Provision/Service/http/ssl.php
  2. 7.x-3.x http/Provision/Service/http/apache/ssl.php
  3. 7.x-3.x http/Provision/Service/http/nginx/ssl.php

The base implementation of the SSL capabale web service.

Classes

Namesort descending Description
Provision_Service_http_ssl The base class for SSL supporting servers.

File

http/Provision/Service/http/ssl.php
View source
  1. <?php
  2. /**
  3. * @file
  4. * The base implementation of the SSL capabale web service.
  5. */
  6. /**
  7. * The base class for SSL supporting servers.
  8. *
  9. * In general, these function the same as normal servers, but have an extra
  10. * port and some extra variables in their templates.
  11. */
  12. class Provision_Service_http_ssl extends Provision_Service_http_public {
  13. protected $ssl_enabled = TRUE;
  14. function default_ssl_port() {
  15. return 443;
  16. }
  17. function init_server() {
  18. parent::init_server();
  19. // SSL Port.
  20. $this->server->setProperty('http_ssl_port', $this->default_ssl_port());
  21. // SSL certificate store.
  22. // The certificates are generated from here, and distributed to the servers,
  23. // as needed.
  24. $this->server->ssld_path = "{$this->server->aegir_root}/config/ssl.d";
  25. // SSL certificate store for this server.
  26. // This server's certificates will be stored here.
  27. $this->server->http_ssld_path = "{$this->server->config_path}/ssl.d";
  28. $this->server->ssl_enabled = 1;
  29. $this->server->ssl_key = 'default';
  30. }
  31. function init_site() {
  32. parent::init_site();
  33. $this->context->setProperty('ssl_enabled', 0);
  34. $this->context->setProperty('ssl_key', NULL);
  35. $this->context->setProperty('ip_addresses', array());
  36. }
  37. function config_data($config = NULL, $class = NULL) {
  38. $data = parent::config_data($config, $class);
  39. $data['http_ssl_port'] = $this->server->http_ssl_port;
  40. if ($config == 'server') {
  41. // Generate a certificate for the default SSL vhost, and retrieve the
  42. // path to the cert and key files. It will be generated if not found.
  43. $certs = $this->get_certificates('default');
  44. $data = array_merge($data, $certs);
  45. }
  46. if ($config == 'site' && $this->context->ssl_enabled) {
  47. foreach ($this->context->ip_addresses as $server => $ip_address) {
  48. if ($server == $this->server->name || '@' . $server == $this->server->name) {
  49. $data['ip_address'] = $ip_address;
  50. break;
  51. }
  52. }
  53. if (!isset($data['ip_address'])) {
  54. drush_log(dt('No proper IP provided by the frontend for server %servername, using wildcard', array('%servername' => $this->server->name)), 'info');
  55. $data['ip_address'] = '*';
  56. }
  57. if ($this->context->ssl_enabled == 2) {
  58. $data['ssl_redirection'] = TRUE;
  59. $data['redirect_url'] = "https://{$this->context->uri}";
  60. }
  61. if ($ssl_key = $this->context->ssl_key) {
  62. // Retrieve the paths to the cert and key files.
  63. // they are generated if not found.
  64. $certs = $this->get_certificates($ssl_key);
  65. $data = array_merge($data, $certs);
  66. }
  67. }
  68. return $data;
  69. }
  70. /**
  71. * Retrieve an array containing the actual files for this ssl_key.
  72. *
  73. * If the files could not be found, this function will proceed to generate
  74. * certificates for the current site, so that the operation can complete
  75. * succesfully.
  76. */
  77. function get_certificates($ssl_key) {
  78. $source_path = "{$this->server->ssld_path}/{$ssl_key}";
  79. $certs['ssl_cert_key_source'] = "{$source_path}/openssl.key";
  80. $certs['ssl_cert_source'] = "{$source_path}/openssl.crt";
  81. foreach ($certs as $cert) {
  82. $exists = provision_file()->exists($cert)->status();
  83. if (!$exists) {
  84. // if any of the files don't exist, regenerate them.
  85. $this->generate_certificates($ssl_key);
  86. // break out of the loop.
  87. break;
  88. }
  89. }
  90. $path = "{$this->server->http_ssld_path}/{$ssl_key}";
  91. $certs['ssl_cert_key'] = "{$path}/openssl.key";
  92. $certs['ssl_cert'] = "{$path}/openssl.crt";
  93. // If a certificate chain file exists, add it.
  94. $chain_cert_source = "{$source_path}/openssl_chain.crt";
  95. if (provision_file()->exists($chain_cert_source)->status()) {
  96. $certs['ssl_chain_cert_source'] = $chain_cert_source;
  97. $certs['ssl_chain_cert'] = "{$path}/openssl_chain.crt";
  98. }
  99. return $certs;
  100. }
  101. /**
  102. * Generate a self-signed certificate for that key.
  103. *
  104. * Because we only generate certificates for sites we make some assumptions
  105. * based on the uri, but this cert may be replaced by the admin if they
  106. * already have an existing certificate.
  107. */
  108. function generate_certificates($ssl_key) {
  109. $path = "{$this->server->ssld_path}/{$ssl_key}";
  110. provision_file()->create_dir($path,
  111. dt("SSL certificate directory for %ssl_key", array(
  112. '%ssl_key' => $ssl_key
  113. )), 0700);
  114. if (provision_file()->exists($path)->status()) {
  115. drush_log(dt('generating 2048 bit RSA key in %path/', array('%path' => $path)));
  116. /*
  117. * according to RSA security and most sites I could read, 1024
  118. * was recommended until 2010-2015 and 2048 is now the
  119. * recommended length for more sensitive data. we are therefore
  120. * taking the safest route.
  121. *
  122. * http://www.javamex.com/tutorials/cryptography/rsa_key_length.shtml
  123. * http://www.vocal.com/cryptography/rsa-key-size-selection/
  124. * https://en.wikipedia.org/wiki/Key_size#Key_size_and_encryption_system
  125. * http://www.redkestrel.co.uk/Articles/CSR.html
  126. */
  127. drush_shell_exec('openssl genrsa -out %s/openssl.key 2048', $path)
  128. || drush_set_error('SSL_KEY_GEN_FAIL', dt('failed to generate SSL key in %path', array('%path' => $path . '/openssl.key')));
  129. // Generate the CSR to make the key certifiable by third parties
  130. $domain = $ssl_key == 'default' ? 'default.invalid' : $this->context->uri;
  131. $ident = "/CN={$domain}/emailAddress=abuse@{$domain}";
  132. drush_shell_exec("openssl req -new -subj '%s' -key %s/openssl.key -out %s/openssl.csr -batch", $ident, $path, $path)
  133. || drush_log(dt('failed to generate signing request for certificate in %path', array('%path' => $path . '/openssl.csr')));
  134. // sign the certificate with itself, generating a self-signed
  135. // certificate. this will make a SHA1 certificate by default in
  136. // current OpenSSL.
  137. drush_shell_exec("openssl x509 -req -days 365 -in %s/openssl.csr -signkey %s/openssl.key -out %s/openssl.crt", $path, $path, $path)
  138. || drush_set_error('SSL_CERT_GEN_FAIL', dt('failed to generate self-signed certificate in %path', array('%path' => $path . '/openssl.crt')));
  139. }
  140. }
  141. /**
  142. * Assign the given site to a certificate to mark its usage.
  143. *
  144. * This is necessary for the backend to figure out when it's okay to
  145. * remove certificates.
  146. *
  147. * Should never fail unless the receipt file cannot be created.
  148. *
  149. * @return the path to the receipt file if allocation succeeded
  150. */
  151. static function assign_certificate_site($ssl_key, $site) {
  152. $path = $site->data['server']->http_ssld_path . "/" . $ssl_key . "/" . $site->uri . ".receipt";
  153. drush_log(dt("registering site %site with SSL certificate %key with receipt file %path", array("%site" => $site->uri, "%key" => $ssl_key, "%path" => $path)));
  154. if (touch($path)) {
  155. return $path;
  156. }
  157. else {
  158. return FALSE;
  159. }
  160. }
  161. /**
  162. * Unallocate this certificate from that site.
  163. *
  164. * @return the path to the receipt file if removal was successful
  165. */
  166. static function free_certificate_site($ssl_key, $site) {
  167. if (empty($ssl_key)) return FALSE;
  168. $ssl_dir = $site->platform->server->http_ssld_path . "/" . $ssl_key . "/";
  169. // Remove the file system reciept we left for this file
  170. if (provision_file()->unlink($ssl_dir . $site->uri . ".receipt")->
  171. succeed(dt("Deleted SSL Certificate association receipt for %site on %server", array(
  172. '%site' => $site->uri,
  173. '%server' => $site->server->remote_host)))->status()) {
  174. if (!Provision_Service_http_ssl::certificate_in_use($ssl_key, $site->server)) {
  175. drush_log(dt("Deleting unused SSL directory: %dir", array('%dir' => $ssl_dir)));
  176. _provision_recursive_delete($ssl_dir);
  177. $site->server->sync($path);
  178. }
  179. return $path;
  180. }
  181. else {
  182. return FALSE;
  183. }
  184. }
  185. /**
  186. * Assign the certificate it's own distinct IP address for this server.
  187. *
  188. * Each certificate needs a unique IP address on each server in order
  189. * to be able to be encrypted.
  190. *
  191. * This code uses the filesystem by touching a reciept file in the
  192. * server's ssl.d directory.
  193. *
  194. * @deprecated this is now based the site URI
  195. * @see assign_certificate_site()
  196. */
  197. static function assign_certificate_ip($ssl_key, $server) {
  198. return FALSE;
  199. }
  200. /**
  201. * Remove the certificate's lock on the server's public IP.
  202. *
  203. * This function will delete the receipt file left behind by
  204. * the assign_certificate_ip script, allowing the IP to be used
  205. * by other certificates.
  206. *
  207. * @deprecated this is now based on the site URI
  208. * @see free_certificate_site()
  209. */
  210. static function free_certificate_ip($ssl_key, $server) {
  211. return FALSE;
  212. }
  213. /**
  214. * Retrieve the status of a certificate on this server.
  215. *
  216. * This is primarily used to know when it's ok to remove the file.
  217. * Each time a config file uses the key on the server, it touches
  218. * a 'receipt' file, and every time the site stops using it,
  219. * the receipt is removed.
  220. *
  221. * This function just checks if any of the files are still present.
  222. */
  223. static function certificate_in_use($ssl_key, $server) {
  224. $pattern = $server->http_ssld_path . "/$ssl_key/*.receipt";
  225. return sizeof(glob($pattern));
  226. }
  227. /**
  228. * Check for an existing record for this IP address.
  229. *
  230. * @deprecated we only use the URI-based allocation now
  231. */
  232. static function get_ip_certificate($ip, $server) {
  233. return FALSE;
  234. }
  235. /**
  236. * Verify server.
  237. */
  238. function verify_server_cmd() {
  239. if ($this->context->type === 'server') {
  240. provision_file()->create_dir($this->server->ssld_path, dt("Central SSL certificate repository."), 0700);
  241. provision_file()->create_dir($this->server->http_ssld_path,
  242. dt("SSL certificate repository for %server",
  243. array('%server' => $this->server->remote_host)), 0700);
  244. $this->sync($this->server->http_ssld_path, array(
  245. 'exclude' => $this->server->http_ssld_path . '/*', // Make sure remote directory is created
  246. ));
  247. $this->sync($this->server->http_ssld_path . '/default');
  248. }
  249. // Call the parent at the end. it will restart the server when it finishes.
  250. parent::verify_server_cmd();
  251. }
  252. }