diff --git a/src/Provision/Command/ServicesCommand.php b/src/Provision/Command/ServicesCommand.php index c7774441f..ae702883f 100644 --- a/src/Provision/Command/ServicesCommand.php +++ b/src/Provision/Command/ServicesCommand.php @@ -99,21 +99,25 @@ public static function getCommandOptions() { // Load all service options $options = Context::getServiceOptions(); - + // For each service type... foreach ($options as $service => $service_name) { - $class = Service::getClassName($service); - - // Load option_documentation() into input options. - foreach (Context::getContextTypeOptions() as $type => $type_name) { - $method = "{$type}_options"; - foreach ($class::$method() as $option => $description) { - $description = "$type_name $service service: $description"; - $inputDefinition[] = new InputOption($option, NULL, InputOption::VALUE_OPTIONAL, $description); + // Load every available service type. + foreach (Context::getServiceTypeOptions($service) as $service_type => $service_name) { + $class = Service::getClassName($service, $service_type); + + Provision::getProvision()->getLogger()->debug("Loading options from $class $service_type"); + + // Load option_documentation() into input options. + foreach (Context::getContextTypeOptions() as $type => $type_name) { + $method = "{$type}_options"; + foreach ($class::$method() as $option => $description) { + $description = "$type_name $service $service_name service: $description"; + $inputDefinition[] = new InputOption($option, NULL, InputOption::VALUE_OPTIONAL, $description); + } } } - } return $inputDefinition; @@ -283,12 +287,12 @@ private function askForServiceProperties($service, $service_type = NULL) { $property = Provision::newProperty($property); } - if ($this->context->hasService($service) && $this->context->getService($service)->getProperty($name)) { + if ($this->context->hasService($service) && $this->context->getService($service)->hasProperty($name) && $this->context->getService($service)->getProperty($name)) { $property->default = $this->context->getService($service)->getProperty($name); } // If option does not exist, ask for it. - if (!empty($this->input->getOption($name))) { + if ($this->input->hasOption($name) && !empty($this->input->getOption($name))) { $properties[$name] = $this->input->getOption($name); $this->io->comment("Using option {$name}={$properties[$name]}"); } diff --git a/src/Provision/ConfigFile.php b/src/Provision/ConfigFile.php index aede2bf8a..7b6baf34a 100644 --- a/src/Provision/ConfigFile.php +++ b/src/Provision/ConfigFile.php @@ -119,10 +119,16 @@ function __construct($context, $service = NULL, $data = array()) { * This is a stub to be implemented by subclasses. */ function process() { + $this->data = []; + if (!empty($this->context->getProperties())) { $this->data = $this->context->getProperties(); } + if (isset($this->service) && !empty($this->service->getProperties())) { + $this->data = array_merge($this->data, $this->service->getProperties()); + } + // @TODO: Remove legacy code. // if (is_object($this->store)) { // $this->data['records'] = array_filter(array_merge($this->store->loaded_records, $this->store->records)); diff --git a/src/Provision/Context/ServerContext.php b/src/Provision/Context/ServerContext.php index 01be1ba54..bcb9d3d5a 100644 --- a/src/Provision/Context/ServerContext.php +++ b/src/Provision/Context/ServerContext.php @@ -58,6 +58,8 @@ function __construct($name, Provision $provision, array $options = []) else { $this->server_config_path = $this->getProperty('server_config_path'); } + + $this->fs = new Filesystem(); } /** @@ -115,4 +117,55 @@ public function verify() $tasks = []; return $tasks; } + + /** + * Run a shell command on this server. + * + * @param $cmd string The command to run + * @param $dir string The directory to run the command in. Defaults to this server's config path. + * @param $return string What to return. Can be 'output' or 'exit'. + * + * @return string + * @throws \Exception + */ + public function shell_exec($command, $dir = NULL, $return = 'output') { + $cwd = getcwd(); + $original_command = $command; + + $tmpdir = sys_get_temp_dir() . '/provision'; + if (!Provision::fs()->exists($tmpdir)){ + Provision::fs()->mkdir($tmpdir); + } + + $datestamp = date('c'); + $tmp_output_file = tempnam($tmpdir, 'task.' . $datestamp . '.output.'); + + $effective_wd = $dir? $dir: + $this->getProperty('server_config_path'); + + if ($this->getProvision()->getOutput()->isVerbose()) { + $this->getProvision()->io()->commandBlock($command, $effective_wd); + } + + // Output and Errors to files. + $command .= "> $tmp_output_file 2>&1"; + + chdir($effective_wd); + exec($command, $output, $exit); + chdir($cwd); + + $output = file_get_contents($tmp_output_file); + + if (!empty($output)){ + if ($this->getProvision()->getOutput()->isVerbose()) { + $this->getProvision()->io()->outputBlock($output); + } + } + + if ($exit != ResultData::EXITCODE_OK) { + throw new \Exception($output); + } + + return ${$return}; + } } diff --git a/src/Provision/Service.php b/src/Provision/Service.php index ed6832462..e76176a1b 100644 --- a/src/Provision/Service.php +++ b/src/Provision/Service.php @@ -203,12 +203,13 @@ protected function writeConfigurations(Context $context = NULL) ); } catch (\Exception $e) { - $context->getProvision()->getLogger()->info( - 'Unable to write {description} to {path}.', [ - 'description' => $config->description, - 'path' => $config->filename(), + throw new \Exception(strtr( + 'Unable to write {description} to {path}: {message}', [ + '{description}' => $config->description, + '{path}' => $config->filename(), + '{message}' => $e->getMessage(), ] - ); + )); $success = FALSE; } } @@ -243,6 +244,29 @@ public function getName() return $this::SERVICE; } + /** + * Return all properties for this context. + * + * @return array + */ + public function getProperties() { + return $this->properties; + } + + /** + * Whether or not this Services has a property. + * + * @param $type + * @return bool + */ + public function hasProperty($name) { + if (isset($this->properties[$name])) { + return TRUE; + } + else { + return FALSE; + } + } /** * Get a specific property. diff --git a/src/Provision/Service/Http/Apache/Configuration/SiteConfigFile.php b/src/Provision/Service/Http/Apache/Configuration/SiteConfigFile.php index 8aed5c224..11dc960d9 100644 --- a/src/Provision/Service/Http/Apache/Configuration/SiteConfigFile.php +++ b/src/Provision/Service/Http/Apache/Configuration/SiteConfigFile.php @@ -48,6 +48,10 @@ function process() { $extra_apache_configs = $this->getContext()->servicesInvoke('extraApacheConfig', [$this]); $this->data['extra_config'] = implode("\n", $extra_apache_configs); + $this->data['aliases'] = []; + $this->data['redirection'] = FALSE; + $this->data['ssl_redirection'] = FALSE; + // if ($this->aliases && !is_array($this->aliases)) { // $this->aliases = explode(",", $this->aliases); // } diff --git a/src/Provision/Service/Http/HttpNginxService.php b/src/Provision/Service/Http/HttpNginxService.php index 6e837c4b7..c5c39bf50 100644 --- a/src/Provision/Service/Http/HttpNginxService.php +++ b/src/Provision/Service/Http/HttpNginxService.php @@ -8,6 +8,12 @@ namespace Aegir\Provision\Service\Http; +use Aegir\Provision\Context\ServerContext; +use Aegir\Provision\Provision; +use Aegir\Provision\Service\Http\Nginx\Configuration\PlatformConfiguration; +use Aegir\Provision\Service\Http\Nginx\Configuration\ServerConfiguration; +use Aegir\Provision\Service\Http\Nginx\Configuration\SiteCommonConfiguration; +use Aegir\Provision\Service\Http\Nginx\Configuration\SiteConfiguration; use Aegir\Provision\Service\HttpService; /** @@ -15,8 +21,267 @@ * * @package Aegir\Provision\Service\Http */ -class HttpNginxService extends HttpService -{ - const SERVICE_TYPE = 'nginx'; - const SERVICE_TYPE_NAME = 'NGINX'; +class HttpNginxService extends HttpService { + + const SERVICE_TYPE = 'nginx'; + + const SERVICE_TYPE_NAME = 'NGINX'; + + /** + * Path to PHP FPM Socket file for php5. + */ + const SOCKET_PATH_PHP5 = '/var/run/php5-fpm.sock'; + + /** + * Path to PHP FPM Socket file for php7. + */ + const SOCKET_PATH_PHP7 = '/var/run/php/php7.0-fpm.sock'; + + /** + * Path to NGINX Control mode file. + */ + const NGINX_CONTROL_MODE_FILE = '/etc/nginx/basic_nginx.conf'; + + /** + * + */ + const BOA_CONFIG_PATH = '/data/conf/global.inc'; + + /** + * HttpNginxService constructor. + * + * Detects NGINX configuration and sets as service properties. + * + * @param $service_config + * @param \Aegir\Provision\Context\ServerContext $provider_context + */ + function __construct($service_config, ServerContext $provider_context) { + parent::__construct($service_config, $provider_context); + + } + + /** + * Run when a `verify` command is invoked. + * + * @TODO: Should we move this to a different function? I don't want to store + * these values in config files since they are read from the system. + * + * Are these values needed in other methods or commands? Is it ok for those + * other methods to invoke verify() themselves if they need these properties? + * + * @return array + */ + public function verify() { + + $nginx_config = $this->provider->shell_exec(self::getNginxExecutable() . ' -V'); + $this->setProperty('nginx_is_modern', preg_match("/nginx\/1\.((1\.(8|9|(1[0-9]+)))|((2|3|4|5|6|7|8|9|[1-9][0-9]+)\.))/", $nginx_config, $match)); + $this->setProperty('nginx_has_etag', preg_match("/nginx\/1\.([12][0-9]|[3]\.([12][0-9]|[3-9]))/", $nginx_config, $match)); + $this->setProperty('nginx_has_http2', preg_match("/http_v2_module/", $nginx_config, $match)); + $this->setProperty('nginx_has_upload_progress', preg_match("/upload/", $nginx_config, $match)); + $this->setProperty('nginx_has_gzip', preg_match("/http_gzip_static_module/", $nginx_config, $match)); + + // Use basic nginx configuration if this control file exists. + if (Provision::fs()->exists(self::NGINX_CONTROL_MODE_FILE)) { + $this->setProperty('nginx_config_mode', 'basic'); + $this->getProvision()->getLogger()->info('Basic Nginx Config Active -SAVE- YES control file found {path}.', [ + 'path' => self::NGINX_CONTROL_MODE_FILE, + ]); + } + else { + $this->setProperty('nginx_config_mode', 'extended'); + $this->getProvision()->getLogger()->info('Extended Nginx Config Active -SAVE- NO control file found {path}.', [ + 'path' => self::NGINX_CONTROL_MODE_FILE, + ]); + } + + // Check if there is php-fpm listening on unix socket, otherwise use port 9000 to connect + $this->setProperty('phpfpm_mode', self::getPhpFpmMode()); + $this->setProperty('phpfpm_socket_path', self::getPhpFpmSocketPath()); + + // @TODO: Work out a way for something like BOA to do this via a plugin. + // Check if there is BOA specific global.inc file to enable extra Nginx locations + if ($this->provider->fs->exists(self::BOA_CONFIG_PATH)) { + $this->setProperty('satellite_mode', 'boa'); + $this->getProvision()->getLogger()->debug('BOA mode detected -SAVE- YES file found {path}.', ['path' => self::BOA_CONFIG_PATH]); + } + else { + $this->setProperty('satellite_mode', 'vanilla'); + $this->getProvision()->getLogger()->debug('Vanilla mode detected -SAVE- NO file found {path}.', ['path' => self::BOA_CONFIG_PATH]); + } + +// // Set correct subdirs_support value on server save +// if (provision_hosting_feature_enabled('subdirs')) { +// $this->server->subdirs_support = TRUE; +// } + + $this->setProperty('server_config_path', $this->provider->server_config_path); + + return parent::verify(); + } + + /** + * No tasks to run + * @return array + */ + function verifyPlatform() { + $tasks = []; + return $tasks; + } + + /** + /** + * Override restart_command task to use our default_restart_cmd() + * + * @TODO: Not sure why this is needed? Why does "self" not return this class's default_restart_cmd()? + * + * @return array + */ + static function server_options() { + $options = parent::server_options(); + $options['restart_command'] = Provision::newProperty() + ->description('The command to reload the web server configuration.') + ->defaultValue(function () { + return self::default_restart_cmd(); + }) + ->required() + ; + $options['php_fpm_sock_location'] = Provision::newProperty() + ->description('Path to PHP FPM socket, or address and port that PHP-FPM is listening on (127.0.0.1:5000). NOTE: If installed using yum or apt, you may already have a configured "upstream" named "php-fpm", if so use "php-fpm" here.') + ->defaultValue(self::getPhpFpmLocation()) + ->hidden() + ; + return $options; + } + + /** + * Returns array of Configuration classes for this service. + * + * @see Provision_Service_http_apache::init_server(); + * + * @return array + */ + public function getConfigurations() + { + $configs['server'][] = ServerConfiguration::class; + $configs['server'][] = SiteCommonConfiguration::class; + $configs['site'][] = SiteConfiguration::class; + return $configs; + } + + /** + * Determine full nginx restart command. + * + * @return string + */ + public static function default_restart_cmd() { + $command = self::getNginxExecutable(); + if ($command != '/etc/init.d/nginx') { + $command .= ' -s'; + } + + return "sudo $command reload"; + } + + /** + * Find the nginx executable and return the path to it. + * + * @return array + */ + public static function getNginxExecutable() { + $command = '/etc/init.d/nginx'; // A proper default for most of the world + $options[] = $command; + // Try to detect the nginx restart command. + foreach (explode(':', $_SERVER['PATH']) as $path) { + $options[] = "$path/nginx"; + } + $options[] = '/usr/sbin/nginx'; + $options[] = '/usr/local/sbin/nginx'; + $options[] = '/usr/local/bin/nginx'; + + foreach ($options as $test) { + if (is_executable($test)) { + return $test; + } + } + } + + /** + * Determines the PHP FPM mode. + * + * @return string + * The mode, either 'socket' or 'port'. + */ + public static function getPhpFpmMode() { + + // Search for socket files or fall back to port mode. + switch (TRUE) { + case Provision::fs()->exists(self::SOCKET_PATH_PHP5): + $mode = 'socket'; + $socket_path = self::SOCKET_PATH_PHP5; + break; + case Provision::fs()->exists(self::SOCKET_PATH_PHP7): + $mode = 'socket'; + $socket_path = self::SOCKET_PATH_PHP7; + break; + default: + $mode = 'port'; + $socket_path = ''; + break; + } + + // Return the discovered mode. + return $mode; + } + + /** + * Gets the PHP FPM unix socket path. + * + * If we're running in port mode, there is no socket path. FALSE would be + * returned in this case. + * + * @return string + * The path, or FALSE if there isn't one. + */ + public static function getPhpFpmSocketPath() { + // Simply return FALSE if we're in port mode. + if (self::getPhpFpmMode() == 'port') { + return FALSE; + } + + // Return the socket path based on the PHP version. + if (strtok(phpversion(), '.') == 7) { + return self::SOCKET_PATH_PHP7; + } + else { + return self::SOCKET_PATH_PHP5; + } + } + + /** + * Find the path to PHP FPM socket from common options. + * + * @return mixed + */ + public static function getPhpFpmLocation() { + $options[] = '/run/php-fpm/www.sock'; + $options[] = '/run/php/php7.2-fpm.sock'; + $options[] = '/run/php/php7.1-fpm.sock'; + $options[] = '/run/php/php7.0-fpm.sock'; + $options[] = '/var/run/php7-fpm.sock'; + $options[] = '/var/run/php5-fpm.sock'; + $options[] = '/var/run/php/php7.2-fpm.sock'; + $options[] = '/var/run/php/php7.1-fpm.sock'; + $options[] = '/var/run/php/php7.0-fpm.sock'; + $options[] = '/opt/remi/php72/root/tmp/php-fpm.sock'; + $options[] = '/opt/remi/php71/root/tmp/php-fpm.sock'; + $options[] = '/opt/remi/php70/root/tmp/php-fpm.sock'; + + foreach ($options as $test) { + if (Provision::fs()->exists($test)) { + return 'unix:' . $test; + } + } + + return '127.0.0.1:5000'; + } } diff --git a/src/Provision/Service/Http/Nginx/Configuration/ServerConfiguration.php b/src/Provision/Service/Http/Nginx/Configuration/ServerConfiguration.php new file mode 100644 index 000000000..95b2b5465 --- /dev/null +++ b/src/Provision/Service/Http/Nginx/Configuration/ServerConfiguration.php @@ -0,0 +1,53 @@ +service->getType()) { + $file = $this->service->getType() . '.conf'; + return $this->service->provider->getProvision()->getConfig()->get('config_path') . '/' . $this->service->provider->name . '/' . $file; + } + else { + return FALSE; + } + } + + function process() { + parent::process(); + + // Run verify to load in nginx properties. + $this->service->verify(); + + $app_dir = $this->getContext()->server_config_path . DIRECTORY_SEPARATOR . $this->service->getType(); + $this->data['http_port'] = $this->service->properties['http_port']; + $this->data['include_statement'] = '# INCLUDE STATEMENT'; + $this->data['http_pred_path'] = "{$app_dir}/pre.d"; + $this->data['http_postd_path'] = "{$app_dir}/post.d"; + $this->data['http_platformd_path'] = "{$app_dir}/platform.d"; + $this->data['http_vhostd_path'] = "{$app_dir}/vhost.d"; + $this->data['extra_config'] = ""; + + $this->fs->mkdir($this->data['http_pred_path']); + $this->fs->mkdir($this->data['http_postd_path']); + $this->fs->mkdir($this->data['http_platformd_path']); + $this->fs->mkdir($this->data['http_vhostd_path']); + + $this->data['script_user'] = $this->service->provider->getProperty('script_user'); + $this->data['aegir_root'] = $this->service->provider->getProperty('aegir_root'); + + $this->data['php_fpm_sock_location'] = $this->service->getProperty('php_fpm_sock_location'); + + } +} \ No newline at end of file diff --git a/src/Provision/Service/Http/Nginx/Configuration/SiteCommonConfiguration.php b/src/Provision/Service/Http/Nginx/Configuration/SiteCommonConfiguration.php new file mode 100644 index 000000000..bd4ccee9e --- /dev/null +++ b/src/Provision/Service/Http/Nginx/Configuration/SiteCommonConfiguration.php @@ -0,0 +1,51 @@ +data['application_name'])) { +// $file = $this->data['application_name'] . '_vhost_common.conf'; +// $legacy_simple_file = $this->data['application_name'] . '_simple_include.conf'; +// $legacy_advanced_file = $this->data['application_name'] . '_advanced_include.conf'; +// // We link both legacy files on the remote server to the right version. +// $cmda = sprintf('ln -sf %s %s', +// escapeshellarg($this->data['server']->include_path . '/' . $file), +// escapeshellarg($this->data['server']->include_path . '/' . $legacy_simple_file) +// ); +// $cmdb = sprintf('ln -sf %s %s', +// escapeshellarg($this->data['server']->include_path . '/' . $file), +// escapeshellarg($this->data['server']->include_path . '/' . $legacy_advanced_file) +// ); +// if ($this->data['server']->shell_exec($cmda)) { +// drush_log(dt("Created legacy_simple_file symlink for %file on %server", array( +// '%file' => $file, +// '%server' => $this->data['server']->remote_host, +// ))); +// }; +// if ($this->data['server']->shell_exec($cmdb)) { +// drush_log(dt("Created legacy_advanced_file symlink for %file on %server", array( +// '%file' => $file, +// '%server' => $this->data['server']->remote_host, +// ))); +// }; +// } + } + + function filename() { + return $this->service->provider->server_config_path . '/nginx_vhost_common.conf'; + } +} \ No newline at end of file diff --git a/src/Provision/Service/Http/Nginx/Configuration/SiteConfiguration.php b/src/Provision/Service/Http/Nginx/Configuration/SiteConfiguration.php new file mode 100644 index 000000000..fb54f9c11 --- /dev/null +++ b/src/Provision/Service/Http/Nginx/Configuration/SiteConfiguration.php @@ -0,0 +1,22 @@ +data['php_fpm_sock_location'] = $this->service->getProperty('php_fpm_sock_location'); + } +} \ No newline at end of file diff --git a/src/Provision/Service/Http/Nginx/Configuration/templates/server.tpl.php b/src/Provision/Service/Http/Nginx/Configuration/templates/server.tpl.php new file mode 100644 index 000000000..a704da603 --- /dev/null +++ b/src/Provision/Service/Http/Nginx/Configuration/templates/server.tpl.php @@ -0,0 +1,215 @@ +# Aegir web server main configuration file + +####################################################### +### nginx.conf main +####################################################### + + + + + + ## Size Limits + client_body_buffer_size 64k; + client_header_buffer_size 32k; + connection_pool_size 256; + fastcgi_buffer_size 128k; + fastcgi_buffers 256 4k; + fastcgi_busy_buffers_size 256k; + fastcgi_temp_file_write_size 256k; + large_client_header_buffers 32 32k; + request_pool_size 4k; + server_names_hash_bucket_size 512; + + ## Timeouts + client_body_timeout 180; + client_header_timeout 180; + send_timeout 180; + lingering_time 30; + lingering_timeout 5; + fastcgi_connect_timeout 10s; + fastcgi_send_timeout 180s; + fastcgi_read_timeout 180s; + + ## Open File Performance + open_file_cache max=8000 inactive=30s; + open_file_cache_valid 99s; + open_file_cache_min_uses 3; + open_file_cache_errors on; + + ## FastCGI Caching + fastcgi_cache_path /var/lib/nginx/speed + levels=2:2 + keys_zone=speed:10m + inactive=15m + max_size=3g; + + ## General Options + ignore_invalid_headers on; + recursive_error_pages on; + reset_timedout_connection on; + fastcgi_intercept_errors on; + + ## SSL performance + ssl_session_cache shared:SSL:10m; + + ## Compression + gzip_buffers 16 8k; + gzip_comp_level 8; + gzip_http_version 1.0; + gzip_min_length 50; + gzip_types + application/atom+xml + application/javascript + application/json + application/rss+xml + application/vnd.ms-fontobject + application/x-font-opentype + application/x-font-ttf + application/x-javascript + application/xhtml+xml + application/xml + application/xml+rss + font/opentype + image/svg+xml + image/x-icon + text/css + text/javascript + text/plain + text/xml; + gzip_vary on; + gzip_proxied any; + + +## Default index files +index index.php index.html; + +## Log Format +log_format main '"$proxy_add_x_forwarded_for" $host [$time_local] ' +'"$request" $status $body_bytes_sent ' +'$request_length $bytes_sent "$http_referer" ' +'"$http_user_agent" $request_time "$gzip_ratio"'; + +client_body_temp_path /var/lib/nginx/body 1 2; +access_log /var/log/nginx/access.log main; + + + +####################################################### +### nginx default maps +####################################################### + +### +### Support separate Speed Booster caches for various mobile devices. +### +map $http_user_agent $device { +default normal; +~*Nokia|BlackBerry.+MIDP|240x|320x|Palm|NetFront|Symbian|SonyEricsson mobile-other; +~*iPhone|iPod|Android|BlackBerry.+AppleWebKit mobile-smart; +~*iPad|Tablet mobile-tablet; +} + +### +### Set a cache_uid variable for authenticated users (by @brianmercer and @perusio, fixed by @omega8cc). +### +map $http_cookie $cache_uid { +default ''; +~SESS[[:alnum:]]+=(?[[:graph:]]+) $session_id; + } + + ### + ### Live switch of $key_uri for Speed Booster cache depending on $args. + ### + map $request_uri $key_uri { + default $request_uri; + ~(?[[:graph:]]+)\?(.*)(utm_|__utm|_campaign|gclid|source=|adv=|req=) $no_args_uri; + } + + ### + ### Deny crawlers. + ### + map $http_user_agent $is_crawler { + default ''; + ~*HTTrack|BrokenLinkCheck|2009042316.*Firefox.*3\.0\.10 is_crawler; + ~*SiteBot|PECL|Automatic|CCBot|BuzzTrack|Sistrix|Offline is_crawler; + ~*SWEB|Morfeus|GSLFbot|HiScan|Riddler|DBot|SEOkicks|MJ12 is_crawler; + ~*PChomebot|Scrap|HTMLParser|Nutch|Mireo|Semrush|Ahrefs is_crawler; + } + + ### + ### Block semalt botnet. + ### + map $http_referer $is_botnet { + default ''; + ~*semalt\.com|kambasoft\.com|savetubevideo\.com|bottlenose\.com|yapoga\.com is_botnet; + ~*descargar-musica-gratis\.net|baixar-musicas-gratis\.com is_botnet; + } + + ### + ### Deny all known bots/spiders on some URIs. + ### + map $http_user_agent $is_bot { + default ''; + ~*crawl|bot|spider|tracker|click|parser|google|yahoo|yandex|baidu|bing is_bot; + } + + ### + ### Deny almost all crawlers under high load. + ### + map $http_user_agent $deny_on_high_load { + default ''; + ~*crawl|spider|tracker|click|parser|google|yahoo|yandex|baidu|bing deny_on_high_load; + } + + ### + ### Deny listed requests for security reasons. + ### + map $args $is_denied { + default ''; + ~*delete.+from|insert.+into|select.+from|union.+select|onload|\.php.+src|system\(.+|document\.cookie|\;|\.\.\/ is_denied; + } + + + ####################################################### + ### nginx default server + ####################################################### + + server { + listen *:; + server_name _; + location / { + return 404; + } + } + + # PHP-FPM FastCGI server + # network or unix domain socket configuration + + upstream provision-php-fpm { + server ; + } + + ####################################################### + ### nginx virtual domains + ####################################################### + + # virtual hosts + include /*; + include /*; + include /*; + include /*; diff --git a/src/Provision/Service/Http/Nginx/Configuration/templates/vhost.tpl.php b/src/Provision/Service/Http/Nginx/Configuration/templates/vhost.tpl.php new file mode 100644 index 000000000..cfa30107a --- /dev/null +++ b/src/Provision/Service/Http/Nginx/Configuration/templates/vhost.tpl.php @@ -0,0 +1,117 @@ +aegir_root; +// $satellite_mode = d('@server_master')->satellite_mode; + // Redirect all aliases to the main http url using separate vhosts blocks to avoid if{} in Nginx. + foreach ($aliases as $alias_url) { + print "# alias redirection virtual host\n"; + print "server {\n"; + print " listen *:{$http_port};\n"; + // if we use redirections, we need to change the redirection + // target to be the original site URL ($this->uri instead of + // $alias_url) + if (isset($redirection_target) && $alias_url == $redirection_target) { + $uri = str_replace('/', '.', $uri); + print " server_name {$uri};\n"; + } else { + $alias_url = str_replace('/', '.', $alias_url); + print " server_name {$alias_url};\n"; + } + print " access_log off;\n"; +// if ($satellite_mode == 'boa') { +// print "\n"; +// print " ###\n"; +// print " ### Allow access to letsencrypt.org ACME challenges directory.\n"; +// print " ###\n"; +// print " location ^~ /.well-known/acme-challenge {\n"; +// print " alias {$aegir_root}/tools/le/.acme-challenges;\n"; +// print " try_files \$uri 404;\n"; +// print " }\n"; +// print "\n"; +// } + print " return 301 \$scheme://{$redirection_target}\$request_uri;\n"; + print "}\n"; + } +} +?> + +server { + include fastcgi_params; + + # Block https://httpoxy.org/ attacks. + fastcgi_param HTTP_PROXY ""; + + fastcgi_param MAIN_SITE_NAME ; + set $main_site_name ""; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + + fastcgi_param db_type ; + fastcgi_param db_name ; + fastcgi_param db_user ; + fastcgi_param db_passwd ; + fastcgi_param db_host ; +server->db_port ? $this->server->db_port : '3306'; +// } +?> + fastcgi_param db_port ; + listen *:; + server_name ; + root ; + +include_path . "/nginx_vhost_common.conf;\n"; + } +} +else { + print " include " . $server_config_path . "/nginx_vhost_common.conf;\n"; +} +//$if_subsite = $this->data['http_subdird_path'] . '/' . $uri; +//if (provision_hosting_feature_enabled('subdirs') && provision_file()->exists($if_subsite)->status()) { +// print " include " . $if_subsite . "/*.conf;\n"; +//} +?> +} diff --git a/src/Provision/Service/Http/Nginx/Configuration/templates/vhost_disabled.tpl.php b/src/Provision/Service/Http/Nginx/Configuration/templates/vhost_disabled.tpl.php new file mode 100644 index 000000000..0e450b806 --- /dev/null +++ b/src/Provision/Service/Http/Nginx/Configuration/templates/vhost_disabled.tpl.php @@ -0,0 +1,18 @@ +satellite_mode) { + $satellite_mode = $server->satellite_mode; +} +?> + +server { + listen *:; + server_name uri . ' ' . implode(' ', str_replace('/', '.', $this->aliases)); ?>; + + root /var/www/nginx-default; + index index.html index.htm; + ### Do not reveal Aegir front-end URL here. + + return 302 platform->server->web_disable_url . '/' . $this->uri ?>; + +} diff --git a/src/Provision/Service/Http/Nginx/Configuration/templates/vhost_include.tpl.php b/src/Provision/Service/Http/Nginx/Configuration/templates/vhost_include.tpl.php new file mode 100644 index 000000000..7bcb0e0e6 --- /dev/null +++ b/src/Provision/Service/Http/Nginx/Configuration/templates/vhost_include.tpl.php @@ -0,0 +1,1330 @@ +script_user) { +// $script_user = $server->script_user; +//} +// +//$aegir_root = drush_get_option('aegir_root'); +//if (!$aegir_root && $server->aegir_root) { +// $aegir_root = $server->aegir_root; +//} +// +//$nginx_config_mode = drush_get_option('nginx_config_mode'); +//if (!$nginx_config_mode && $server->nginx_config_mode) { +// $nginx_config_mode = $server->nginx_config_mode; +//} +// +//$phpfpm_mode = drush_get_option('phpfpm_mode'); +//if (!$phpfpm_mode && $server->phpfpm_mode) { +// $phpfpm_mode = $server->phpfpm_mode; +//} + +// We can use $server here once we have proper inheritance. +// See Provision_Service_http_nginx_ssl for details. +//$phpfpm_socket_path = Provision_Service_http_nginx::getPhpFpmSocketPath(); +// +//$nginx_is_modern = drush_get_option('nginx_is_modern'); +//if (!$nginx_is_modern && $server->nginx_is_modern) { +// $nginx_is_modern = $server->nginx_is_modern; +//} +// +//$nginx_has_etag = drush_get_option('nginx_has_etag'); +//if (!$nginx_has_etag && $server->nginx_has_etag) { +// $nginx_has_etag = $server->nginx_has_etag; +//} +// +//$nginx_has_http2 = drush_get_option('nginx_has_http2'); +//if (!$nginx_has_http2 && $server->nginx_has_http2) { +// $nginx_has_http2 = $server->nginx_has_http2; +//} +// +//$nginx_has_gzip = drush_get_option('nginx_has_gzip'); +//if (!$nginx_has_gzip && $server->nginx_has_gzip) { +// $nginx_has_gzip = $server->nginx_has_gzip; +//} +// +//$nginx_has_upload_progress = drush_get_option('nginx_has_upload_progress'); +//if (!$nginx_has_upload_progress && $server->nginx_has_upload_progress) { +// $nginx_has_upload_progress = $server->nginx_has_upload_progress; +//} +// +//$satellite_mode = drush_get_option('satellite_mode'); +//if (!$satellite_mode && $server->satellite_mode) { +// $satellite_mode = $server->satellite_mode; +//} +?> +####################################################### + +### nginx.conf site level extended vhost include start + +### nginx.conf site level basic vhost include start + +####################################################### + +### +### Use the main site name if available, instead of +### potentially virtual server_name when alias is set +### as redirection target. See #2358977 for details. +### +if ($main_site_name = '') { + set $main_site_name "$server_name"; +} + + +set $nocache_details "Cache"; + + +### +### Deny crawlers. +### +if ($is_crawler) { + return 403; +} + +### +### Block semalt botnet. +### +if ($is_botnet) { + return 403; +} + +### +### Include high load protection config if exists. +### +include /data/conf/nginx_high_load.c*; + + +### +### Deny not compatible request methods without 405 response. +### +if ( $request_method !~ ^(?:GET|HEAD|POST|PUT|DELETE|OPTIONS)$ ) { + return 403; +} + + +### +### Deny listed requests for security reasons. +### +if ($is_denied) { + return 403; +} + +### +### Add recommended HTTP headers +### +add_header Access-Control-Allow-Origin *; +add_header X-Content-Type-Options nosniff; +add_header X-XSS-Protection "1; mode=block"; + + + +### +### Force clean URLs for Drupal 8. +### +rewrite ^/index.php/(.*)$ $scheme://$host/$1 permanent; + +### +### Include high level local configuration override if exists. +### +include /config/server_master/nginx/post.d/nginx_force_include*; + +### +### Include PHP-FPM version override logic if exists. +### +include /config/server_master/nginx/post.d/fpm_include*; + +### +### Allow to use non-default PHP-FPM version for the site +### listed in the special include file. +### +if ($user_socket = '') { + set $user_socket ""; +} + + +### +### HTTPRL standard support. +### +location ^~ /httprl_async_function_callback { + location ~* ^/httprl_async_function_callback { + access_log off; + set $nocache_details "Skip"; + try_files $uri @nobots; + } +} + +### +### HTTPRL test mode support. +### +location ^~ /admin/httprl-test { + location ~* ^/admin/httprl-test { + access_log off; + set $nocache_details "Skip"; + try_files $uri @nobots; + } +} + +### +### CDN Far Future expiration support. +### +location ^~ /cdn/farfuture/ { + tcp_nodelay off; + access_log off; + log_not_found off; + + etag off; + + add_header ETag ""; + + gzip_http_version 1.0; + if_modified_since exact; + set $nocache_details "Skip"; + location ~* ^/cdn/farfuture/.+\.(?:css|js|jpe?g|gif|png|ico|bmp|svg|swf|pdf|docx?|xlsx?|pptx?|tiff?|txt|rtf|class|otf|ttf|woff|eot|less)$ { + expires max; + add_header X-Header "CDN Far Future Generator 1.0"; + add_header Cache-Control "no-transform, public"; + add_header Last-Modified "Wed, 20 Jan 1988 04:20:42 GMT"; + add_header Access-Control-Allow-Origin *; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + rewrite ^/cdn/farfuture/[^/]+/[^/]+/(.+)$ /$1 break; + try_files $uri @nobots; + } + location ~* ^/cdn/farfuture/ { + expires epoch; + add_header X-Header "CDN Far Future Generator 1.1"; + add_header Cache-Control "private, must-revalidate, proxy-revalidate"; + add_header Access-Control-Allow-Origin *; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + rewrite ^/cdn/farfuture/[^/]+/[^/]+/(.+)$ /$1 break; + try_files $uri @nobots; + } + try_files $uri @nobots; +} + + +### +### If favicon else return error 204. +### +location = /favicon.ico { + access_log off; + log_not_found off; + expires 30d; + try_files /sites/$main_site_name/files/favicon.ico $uri =204; +} + +### +### Support for https://drupal.org/project/robotstxt module +### and static file in the sites/domain/files directory. +### +location = /robots.txt { + access_log off; + log_not_found off; + + try_files /sites/$main_site_name/files/$host.robots.txt /sites/$main_site_name/files/robots.txt $uri @cache; + + try_files /sites/$main_site_name/files/$host.robots.txt /sites/$main_site_name/files/robots.txt $uri @drupal; + +} + + +### +### Allow local access to the FPM status page. +### +location = /fpm-status { + access_log off; + allow 127.0.0.1; + deny all; + fastcgi_pass provision-php-fpm; +} + +### +### Allow local access to the FPM ping URI. +### +location = /fpm-ping { + access_log off; + allow 127.0.0.1; + deny all; + fastcgi_pass provision-php-fpm; +} + + + +### +### Allow local access to support wget method in Aegir settings +### for running sites cron. +### +location = /cron.php { + tcp_nopush off; + keepalive_requests 0; + + allow 127.0.0.1; + deny all; + + try_files $uri =404; + fastcgi_pass provision-php-fpm; +} + +### +### Allow local access to support wget method in Aegir settings +### for running sites cron in Drupal 8. +### +location ^~ /cron/ { + + allow 127.0.0.1; + deny all; + + + set $nocache_details "Skip"; + + try_files $uri @drupal; +} + +### +### Send search to php-fpm early so searching for node.js will work. +### Deny bots on search uri. +### +location ^~ /search { + location ~* ^/search { + if ($is_bot) { + return 403; + } + try_files $uri @cache; + } +} + +### +### Support for https://drupal.org/project/js module. +### +location ^~ /js/ { + location ~* ^/js/ { + if ($is_bot) { + return 403; + } + rewrite ^/(.*)$ /js.php?q=$1 last; + } +} + + +### +### Upload progress support. +### https://drupal.org/project/filefield_nginx_progress +### http://github.com/masterzen/nginx-upload-progress-module +### +location ~ (?.*)/x-progress-id:(?\d*) { + access_log off; + rewrite ^ $upload_form_uri?X-Progress-ID=$upload_id; +} +location ^~ /progress { + access_log off; + upload_progress_json_output; + report_uploads uploads; +} + + + +### +### Deny access to Hostmaster web/db server node. +### It is still possible to edit or break web/db server +### node at /node/2/edit, if you know what are you doing. +### +location ^~ /hosting/c/server_master { + if ($cache_uid = '') { + return 403; + } + if ($is_bot) { + return 403; + } + access_log off; + return 301 $scheme://$host/hosting/sites; +} + +### +### Deny access to Hostmaster db server node. +### It is still possible to edit or break db server +### node at /node/4/edit, if you know what are you doing. +### +location ^~ /hosting/c/server_localhost { + if ($cache_uid = '') { + return 403; + } + if ($is_bot) { + return 403; + } + access_log off; + return 301 $scheme://$host/hosting/sites; +} + + +### +### Fix for #2005116 +### +location ^~ /hosting/sites { + if ($is_bot) { + return 403; + } + access_log off; + set $nocache_details "Skip"; + try_files $uri @drupal; +} + +### +### Fix for Aegir & .info .pl domain extensions. +### +location ^~ /hosting { + if ($is_bot) { + return 403; + } + access_log off; + set $nocache_details "Skip"; + try_files $uri @cache; +} + + +### +### Deny cache details display. +### +location ^~ /admin/settings/performance/cache-backend { + access_log off; + return 301 $scheme://$host/admin/settings/performance; +} + +### +### Deny cache details display. +### +location ^~ /admin/config/development/performance/redis { + access_log off; + return 301 $scheme://$host/admin/config/development/performance; +} + + +### +### Support for backup_migrate module download/restore/delete actions. +### +location ^~ /admin { + if ($is_bot) { + return 403; + } + access_log off; + set $nocache_details "Skip"; + try_files $uri @drupal; +} + +### +### Avoid caching /civicrm* and protect it from bots. +### +location ^~ /civicrm { + if ($is_bot) { + return 403; + } + access_log off; + set $nocache_details "Skip"; + try_files $uri @drupal; +} + +### +### Support for audio module. +### +location ^~ /audio/download { + location ~* ^/audio/download/.*/.*\.(?:mp3|mp4|m4a|ogg)$ { + if ($is_bot) { + return 403; + } + tcp_nopush off; + access_log off; + log_not_found off; + set $nocache_details "Skip"; + try_files $uri @drupal; + } +} + + +### +### Deny listed requests for security reasons. +### +location ~* (\.(?:git.*|htaccess|engine|config|inc|ini|info|install|make|module|profile|test|pl|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\..*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock))$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig\.save))$ { + access_log off; + return 404; +} + +### +### Deny listed requests for security reasons. +### +location ~* /(?:modules|themes|libraries)/.*\.(?:txt|md)$ { + access_log off; + return 404; +} + +### +### Deny listed requests for security reasons. +### +location ~* ^/sites/.*/files/civicrm/(?:ConfigAndLog|custom|upload|templates_c) { + access_log off; + return 404; +} + + +### +### Deny often flooded URI for performance reasons +### +location = /autodiscover/autodiscover.xml { + access_log off; + return 404; +} + +### +### Deny some not supported URI like cgi-bin on the Nginx level. +### +location ~* (?:cgi-bin|vti-bin) { + access_log off; + return 404; +} + +### +### Deny bots on some weak modules uri. +### +location ~* (?:validation|aggregator|vote_up_down|captcha|vbulletin|glossary/) { + if ($is_bot) { + return 403; + } + access_log off; + try_files $uri @cache; +} + +### +### Responsive Images support. +### https://drupal.org/project/responsive_images +### +location ~* \.r\.(?:jpe?g|png|gif) { + if ( $http_cookie ~* "rwdimgsize=large" ) { + rewrite ^/(.*)/mobile/(.*)\.r(\.(?:jpe?g|png|gif))$ /$1/desktop/$2$3 last; + } + rewrite ^/(.*)\.r(\.(?:jpe?g|png|gif))$ /$1$2 last; + access_log off; + set $nocache_details "Skip"; + try_files $uri @drupal; +} + +### +### Adaptive Image Styles support. +### https://drupal.org/project/ais +### +location ~* /(?:.+)/files/styles/adaptive/(?:.+)$ { + if ( $http_cookie ~* "ais=(?[a-z0-9-_]+)" ) { + rewrite ^/(.+)/files/styles/adaptive/(.+)$ /$1/files/styles/$ais_cookie/$2 last; + } + access_log off; + set $nocache_details "Skip"; + try_files $uri @drupal; +} + + +### +### The files/styles support. +### +location ~* /sites/.*/files/styles/(.*)$ { + access_log off; + log_not_found off; + expires 30d; + + set $nocache_details "Skip"; + + try_files /sites/$main_site_name/files/styles/$1 $uri @drupal; +} + +### +### The s3/files/styles (s3fs) support. +### +location ~* /s3/files/styles/(.*)$ { + access_log off; + log_not_found off; + expires 30d; + + set $nocache_details "Skip"; + + try_files /sites/$main_site_name/files/styles/$1 $uri @drupal; +} + +### +### The files/imagecache support. +### +location ~* /sites/.*/files/imagecache/(.*)$ { + access_log off; + log_not_found off; + expires 30d; + + # fix common problems with old paths after import from standalone to Aegir multisite + rewrite ^/sites/(.*)/files/imagecache/(.*)/sites/default/files/(.*)$ /sites/$main_site_name/files/imagecache/$2/$3 last; + rewrite ^/sites/(.*)/files/imagecache/(.*)/files/(.*)$ /sites/$main_site_name/files/imagecache/$2/$3 last; + set $nocache_details "Skip"; + + try_files /sites/$main_site_name/files/imagecache/$1 $uri @drupal; +} + +### +### Send requests with /external/ and /system/ URI keywords to @drupal. +### +location ~* /(?:external|system)/ { + access_log off; + log_not_found off; + expires 30d; + + set $nocache_details "Skip"; + + try_files $uri @drupal; +} + +### +### Deny direct access to backups. +### +location ~* ^/sites/.*/files/backup_migrate/ { + access_log off; + deny all; +} + +### +### Deny direct access to config files in Drupal 8. +### +location ~* ^/sites/.*/files/config_.* { + access_log off; + deny all; +} + + +### +### Include local configuration override if exists. +### +include /config/server_master/nginx/post.d/nginx_vhost_include*; + + + +### +### Private downloads are always sent to the drupal backend. +### Note: this location doesn't work with X-Accel-Redirect. +### +location ~* ^/sites/.*/files/private/ { + if ($is_bot) { + return 403; + } + access_log off; + rewrite ^/sites/.*/files/private/(.*)$ $scheme://$host/system/files/private/$1 permanent; + set $nocache_details "Skip"; + try_files $uri @drupal; +} + + +### +### Deny direct access to private downloads in sites/domain/private. +### Note: this location works with X-Accel-Redirect. +### +location ~* ^/sites/.*/private/ { + internal; + + if ($is_bot) { + return 403; + } + + access_log off; +} + + +### +### Deny direct access to private downloads also for short, rewritten URLs. +### Note: this location works with X-Accel-Redirect. +### +location ~* /files/private/ { + internal; + if ($is_bot) { + return 403; + } + access_log off; +} + +### +### Wysiwyg Fields support. +### +location ~* wysiwyg_fields/(?:plugins|scripts)/.*\.(?:js|css) { + access_log off; + log_not_found off; + try_files $uri @nobots; +} + +### +### Advagg_css and Advagg_js support. +### +location ~* files/advagg_(?:css|js)/ { + expires max; + access_log off; + + etag off; + + add_header ETag ""; + + rewrite ^/files/advagg_(.*)/(.*)$ /sites/$main_site_name/files/advagg_$1/$2 last; + add_header X-Header "AdvAgg Generator 2.0"; + add_header Cache-Control "max-age=31449600, no-transform, public"; + add_header Access-Control-Allow-Origin *; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + set $nocache_details "Skip"; + try_files $uri @nobots; +} + +### +### Make css files compatible with boost caching. +### +location ~* \.css$ { + if ( $request_method = POST ) { + return 405; + } + if ( $cache_uid ) { + return 405; + } + error_page 405 = @uncached; + access_log off; + tcp_nodelay off; + expires max; #if using aggregator + try_files /cache/perm/$host${uri}_.css $uri =404; +} + +### +### Make js files compatible with boost caching. +### +location ~* \.(?:js|htc)$ { + if ( $request_method = POST ) { + return 405; + } + if ( $cache_uid ) { + return 405; + } + error_page 405 = @uncached; + access_log off; + tcp_nodelay off; + expires max; # if using aggregator + try_files /cache/perm/$host${uri}_.js $uri =404; +} + +### +### Support for static .json files with fast 404 +Boost compatibility. +### +location ~* ^/sites/.*/files/.*\.json$ { + if ( $cache_uid ) { + return 405; + } + error_page 405 = @uncached; + access_log off; + tcp_nodelay off; + expires max; ### if using aggregator + try_files /cache/normal/$host${uri}_.json $uri =404; +} + +### +### Support for dynamic .json requests. +### +location ~* \.json$ { + try_files $uri @cache; +} + +### +### Helper location to bypass boost static files cache for logged in users. +### +location @uncached { + access_log off; + expires max; # max if using aggregator, otherwise sane expire time +} + + +### +### Map /files/ shortcut early to avoid overrides in other locations. +### +location ^~ /files/ { + + ### + ### Sub-location to support files/styles with short URIs. + ### + location ~* /files/styles/(.*)$ { + access_log off; + log_not_found off; + expires 30d; + + set $nocache_details "Skip"; + + rewrite ^/files/(.*)$ /sites/$main_site_name/files/$1 last; + try_files /sites/$main_site_name/files/styles/$1 $uri @drupal; + } + + ### + ### Sub-location to support files/imagecache with short URIs. + ### + location ~* /files/imagecache/(.*)$ { + access_log off; + log_not_found off; + expires 30d; + + # fix common problems with old paths after import from standalone to Aegir multisite + rewrite ^/files/imagecache/(.*)/sites/default/files/(.*)$ /sites/$main_site_name/files/imagecache/$1/$2 last; + rewrite ^/files/imagecache/(.*)/files/(.*)$ /sites/$main_site_name/files/imagecache/$1/$2 last; + set $nocache_details "Skip"; + + rewrite ^/files/(.*)$ /sites/$main_site_name/files/$1 last; + try_files /sites/$main_site_name/files/imagecache/$1 $uri @drupal; + } + + location ~* ^.+\.(?:pdf|jpe?g|gif|png|ico|bmp|svg|swf|docx?|xlsx?|pptx?|tiff?|txt|rtf|cgi|bat|pl|dll|class|otf|ttf|woff|eot|less|avi|mpe?g|mov|wmv|mp3|ogg|ogv|wav|midi|zip|tar|t?gz|rar|dmg|exe|apk|pxl|ipa|css|js)$ { + expires 30d; + tcp_nodelay off; + access_log off; + log_not_found off; + rewrite ^/files/(.*)$ /sites/$main_site_name/files/$1 last; + try_files $uri =404; + } + + try_files $uri @cache; + + try_files $uri @drupal; + +} + +### +### Map /downloads/ shortcut early to avoid overrides in other locations. +### +location ^~ /downloads/ { + location ~* ^.+\.(?:pdf|jpe?g|gif|png|ico|bmp|svg|swf|docx?|xlsx?|pptx?|tiff?|txt|rtf|cgi|bat|pl|dll|class|otf|ttf|woff|eot|less|avi|mpe?g|mov|wmv|mp3|ogg|ogv|wav|midi|zip|tar|t?gz|rar|dmg|exe|apk|pxl|ipa)$ { + expires 30d; + tcp_nodelay off; + access_log off; + log_not_found off; + rewrite ^/downloads/(.*)$ /sites/$main_site_name/files/downloads/$1 last; + try_files $uri =404; + } + + try_files $uri @cache; + + try_files $uri @drupal; + +} + +### +### Serve & no-log static files & images directly, +### without all standard drupal rewrites, php-fpm etc. +### +location ~* ^.+\.(?:jpe?g|gif|png|ico|bmp|svg|swf|docx?|xlsx?|pptx?|tiff?|txt|rtf|cgi|bat|pl|dll|class|otf|ttf|woff|eot|less|mp3|wav|midi)$ { + expires 30d; + tcp_nodelay off; + access_log off; + log_not_found off; + rewrite ^/images/(.*)$ /sites/$main_site_name/files/images/$1 last; + rewrite ^/.+/sites/.+/files/(.*)$ /sites/$main_site_name/files/$1 last; + try_files $uri =404; +} + +### +### Serve bigger media/static/archive files directly, +### without all standard drupal rewrites, php-fpm etc. +### +location ~* ^.+\.(?:avi|mpe?g|mov|wmv|ogg|ogv|zip|tar|t?gz|rar|dmg|exe|apk|pxl|ipa)$ { + expires 30d; + tcp_nodelay off; + tcp_nopush off; + access_log off; + log_not_found off; + rewrite ^/.+/sites/.+/files/(.*)$ /sites/$main_site_name/files/$1 last; + try_files $uri =404; +} + +### +### Serve & no-log some static files directly, +### but only from the files directory to not break +### dynamically created pdf files or redirects for +### legacy URLs with asp/aspx extension. +### +location ~* ^/sites/.+/files/.+\.(?:pdf|aspx?)$ { + expires 30d; + tcp_nodelay off; + access_log off; + log_not_found off; + try_files $uri =404; +} + + +### +### Pseudo-streaming server-side support for Flash Video (FLV) files. +### +location ~* ^.+\.flv$ { + flv; + tcp_nodelay off; + tcp_nopush off; + expires 30d; + access_log off; + log_not_found off; + try_files $uri =404; +} + +### +### Pseudo-streaming server-side support for H.264/AAC files. +### +location ~* ^.+\.(?:mp4|m4a)$ { + mp4; + mp4_buffer_size 1m; + mp4_max_buffer_size 5m; + tcp_nodelay off; + tcp_nopush off; + expires 30d; + access_log off; + log_not_found off; + try_files $uri =404; +} + + +### +### Serve & no-log some static files as is, without forcing default_type. +### +location ~* /(?:cross-?domain)\.xml$ { + access_log off; + tcp_nodelay off; + expires 30d; + try_files $uri =404; +} + + +### +### Allow some known php files (like serve.php in the ad module). +### +location ~* /(?:modules|libraries)/(?:contrib/)?(?:ad|tinybrowser|f?ckeditor|tinymce|wysiwyg_spellcheck|ecc|civicrm|fbconnect|radioactivity)/.*\.php$ { + + limit_conn limreq 88; + + tcp_nopush off; + keepalive_requests 0; + access_log off; + if ($is_bot) { + return 403; + } + try_files $uri =404; + + fastcgi_pass provision-php-fpm; +} + +### +### Deny crawlers and never cache known AJAX requests. +### +location ~* /(?:ahah|ajax|batch|autocomplete|done|progress/|x-progress-id|js/.*) { + if ($is_bot) { + return 403; + } + access_log off; + log_not_found off; + + set $nocache_details "Skip"; + try_files $uri @nobots; + + try_files $uri @drupal; + +} + +### +### Serve & no-log static helper files used in some wysiwyg editors. +### +location ~* ^/sites/.*/(?:modules|libraries)/(?:contrib/)?(?:tinybrowser|f?ckeditor|tinymce|flowplayer|jwplayer|videomanager)/.*\.(?:html?|xml)$ { + if ($is_bot) { + return 403; + } + access_log off; + tcp_nodelay off; + expires 30d; + try_files $uri =404; +} + +### +### Serve & no-log any not specified above static files directly. +### +location ~* ^/sites/.*/files/ { + access_log off; + tcp_nodelay off; + expires 30d; + try_files $uri =404; +} + +### +### Make feeds compatible with boost caching and set correct mime type. +### +location ~* \.xml$ { + location ~* ^/autodiscover/autodiscover\.xml { + access_log off; + return 400; + } + if ( $request_method = POST ) { + return 405; + } + if ( $cache_uid ) { + return 405; + } + error_page 405 = @drupal; + access_log off; + add_header X-Header "Boost Citrus 1.0"; + add_header Expires "Tue, 24 Jan 1984 08:00:00 GMT"; + add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0"; + add_header Access-Control-Allow-Origin *; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + charset utf-8; + types { } + default_type text/xml; + try_files /cache/normal/$host${uri}_.xml /cache/normal/$host${uri}_.html $uri @drupal; +} + +### +### Deny bots on never cached uri. +### +location ~* ^/(?:.*/)?(?:admin|user|cart|checkout|logout|comment/reply) { + if ($is_bot) { + return 403; + } + access_log off; + set $nocache_details "Skip"; + try_files $uri @drupal; +} + +### +### Protect from DoS attempts on never cached uri. +### +location ~* ^/(?:.*/)?(?:node/[0-9]+/edit|node/add) { + if ($is_bot) { + return 403; + } + access_log off; + set $nocache_details "Skip"; + try_files $uri @drupal; +} + +### +### Protect from DoS attempts on never cached uri. +### +location ~* ^/(?:.*/)?(?:node/[0-9]+/delete|approve) { + if ($cache_uid = '') { + return 403; + } + if ($is_bot) { + return 403; + } + access_log off; + set $nocache_details "Skip"; + try_files $uri @drupal; +} + + +### +### Support for ESI microcaching: http://groups.drupal.org/node/197478. +### +### This may enhance not only anonymous visitors, but also +### logged in users experience, as it allows you to separate +### microcache for ESI/SSI includes (valid for just 5 seconds) +### from both default Speed Booster cache for anonymous visitors +### (valid by default for 10s or 1h, unless purged on demand via +### recently introduced Purge/Expire modules) and also from +### Speed Booster cache per logged in user (valid for 10 seconds). +### +### Now you have three different levels of Speed Booster cache +### to leverage and deliver the 'live content' experience for +### all visitors, and still protect your server from DoS or +### simply high load caused by unexpected high traffic etc. +### +location ~ ^/(?esi/.*)"$ { + ssi on; + ssi_silent_errors on; + internal; + limit_conn limreq 888; + add_header X-Device "$device"; + add_header X-Speed-Micro-Cache "$upstream_cache_status"; + add_header X-Speed-Micro-Cache-Expire "5s"; + add_header X-NoCache "$nocache_details"; + add_header X-GeoIP-Country-Code "$geoip_country_code"; + add_header X-GeoIP-Country-Name "$geoip_country_name"; + add_header X-This-Proto "$http_x_forwarded_proto"; + add_header X-Server-Name "$main_site_name"; + add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0"; + add_header Access-Control-Allow-Origin *; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + ### + ### Set correct, local $uri. + ### + fastcgi_param QUERY_STRING q=$esi; + fastcgi_param SCRIPT_FILENAME $document_root/index.php; + fastcgi_pass provision-php-fpm; + + ### + ### Use Nginx cache for all visitors. + ### + set $nocache ""; + if ( $http_cookie ~* "NoCacheID" ) { + set $nocache "NoCache"; + } + fastcgi_cache speed; + fastcgi_cache_methods GET HEAD; + fastcgi_cache_min_uses 1; + fastcgi_cache_key "$scheme$is_bot$device$host$request_method$key_uri$cache_uid$http_x_forwarded_proto$sent_http_x_local_proto$cookie_respimg"; + fastcgi_cache_valid 200 5s; + fastcgi_cache_valid 301 1m; + fastcgi_cache_valid 302 403 404 1s; + fastcgi_cache_lock on; + fastcgi_ignore_headers Cache-Control Expires; + fastcgi_pass_header Set-Cookie; + fastcgi_pass_header X-Accel-Expires; + fastcgi_pass_header X-Accel-Redirect; + fastcgi_no_cache $cookie_NoCacheID $http_authorization $http_pragma $nocache; + fastcgi_cache_bypass $cookie_NoCacheID $http_authorization $http_pragma $nocache; + fastcgi_cache_use_stale error http_500 http_503 invalid_header timeout updating; + tcp_nopush off; + keepalive_requests 0; + expires epoch; +} + +### +### Workaround for https://www.drupal.org/node/2599326. +### +if ( $args ~* "/autocomplete/" ) { + return 405; +} +error_page 405 = @drupal; + +### +### Rewrite legacy requests with /index.php to extension-free URL. +### +if ( $args ~* "^q=(?.*)" ) { + rewrite ^/index.php$ $scheme://$host/?q=$query_value? permanent; +} + + + +### +### Catch all unspecified requests. +### +location / { + + + if ( $http_user_agent ~* wget ) { + return 403; + } + + try_files $uri @cache; + + try_files $uri @drupal; + +} + + +### +### Boost compatible cache check. +### +location @cache { + if ( $request_method = POST ) { + set $nocache_details "Method"; + return 405; + } + if ( $args ~* "nocache=1" ) { + set $nocache_details "Args"; + return 405; + } + if ( $sent_http_x_force_nocache = "YES" ) { + set $nocache_details "Skip"; + return 405; + } + if ( $http_cookie ~* "NoCacheID" ) { + set $nocache_details "AegirCookie"; + return 405; + } + if ( $cache_uid ) { + set $nocache_details "DrupalCookie"; + return 405; + } + error_page 405 = @drupal; + add_header X-Header "Boost Citrus 1.0"; + add_header Expires "Tue, 24 Jan 1984 08:00:00 GMT"; + add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0"; + add_header Access-Control-Allow-Origin *; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + charset utf-8; + try_files /cache/normal/$host${uri}_$args.html @drupal; +} + + +### +### Send all not cached requests to drupal with clean URLs support. +### +location @drupal { + + error_page 418 = @nobots; + if ($args) { + return 418; + } + + ### + ### For Drupal >= 7 + ### + if ($sent_http_x_generator) { + add_header X-Info-Gen "Modern"; + rewrite ^ /index.php?$query_string last; + } + ### + ### For Drupal <= 6 + ### + rewrite ^/(.*)$ /index.php?q=$1 last; +} + + +### +### Special location for bots custom restrictions; can be overridden. +### +location @nobots { + ### + ### Support for Accelerated Mobile Pages (AMP) when bots are redirected below + ### + # if ( $query_string ~ "^amp$" ) { + # rewrite ^/(.*)$ /index.php?q=$1 last; + # } + + ### + ### Send all known bots to $args free URLs (optional) + ### + # if ($is_bot) { + # return 301 $scheme://$host$request_uri; + # } + + ### + ### Return 404 on special PHP URLs to avoid revealing version used, + ### even indirectly. See also: https://drupal.org/node/2116387 + ### + if ( $args ~* "=PHP[A-Z0-9]{8}-" ) { + return 404; + } + + ### + ### For Drupal >= 7 + ### + if ($sent_http_x_generator) { + add_header X-Info-Gen "Modern"; + rewrite ^ /index.php?$query_string last; + } + ### + ### For Drupal <= 6 + ### + rewrite ^/(.*)$ /index.php?q=$1 last; +} + +### +### Send all non-static requests to php-fpm, restricted to known php file. +### +location = /index.php { + + limit_conn limreq 88; + add_header X-Device "$device"; + add_header X-GeoIP-Country-Code "$geoip_country_code"; + add_header X-GeoIP-Country-Name "$geoip_country_name"; + + + add_header X-Speed-Cache "$upstream_cache_status"; + add_header X-Speed-Cache-UID "$cache_uid"; + add_header X-Speed-Cache-Key "$key_uri"; + add_header X-NoCache "$nocache_details"; + add_header X-This-Proto "$http_x_forwarded_proto"; + add_header X-Server-Name "$main_site_name"; + add_header Access-Control-Allow-Origin *; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + + add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0"; + tcp_nopush off; + keepalive_requests 0; + try_files $uri =404; ### check for existence of php file first + fastcgi_pass provision-php-fpm; + + track_uploads uploads 60s; ### required for upload progress + + ### + ### Use Nginx cache for all visitors. + ### + set $nocache ""; + if ( $nocache_details ~ (?:AegirCookie|Args|Skip) ) { + set $nocache "NoCache"; + } + fastcgi_cache speed; + fastcgi_cache_methods GET HEAD; ### Nginx default, but added for clarity + fastcgi_cache_min_uses 1; + fastcgi_cache_key "$scheme$is_bot$device$host$request_method$key_uri$cache_uid$http_x_forwarded_proto$sent_http_x_local_proto$cookie_respimg"; + fastcgi_cache_valid 200 10s; + fastcgi_cache_valid 301 1m; + fastcgi_cache_valid 302 403 404 1s; + fastcgi_cache_lock on; + fastcgi_ignore_headers Cache-Control Expires; + fastcgi_pass_header Set-Cookie; + fastcgi_pass_header X-Accel-Expires; + fastcgi_pass_header X-Accel-Redirect; + fastcgi_no_cache $cookie_NoCacheID $http_authorization $http_pragma $nocache; + fastcgi_cache_bypass $cookie_NoCacheID $http_authorization $http_pragma $nocache; + fastcgi_cache_use_stale error http_500 http_503 invalid_header timeout updating; +} + + +### +### Send other known php requests/files to php-fpm without any caching. +### + +location ~* ^/(?:core/)?(?:boost_stats|rtoc|js)\.php$ { + +location ~* ^/(?:index|cron|boost_stats|update|authorize|xmlrpc)\.php$ { + + + limit_conn limreq 88; + if ($is_bot) { + return 404; + } + + tcp_nopush off; + keepalive_requests 0; + access_log off; + try_files $uri =404; ### check for existence of php file first + fastcgi_pass provision-php-fpm; +} + + +### +### Allow access to /authorize.php and /update.php only for logged in admin user. +### +location ~* ^/(?:core/)?(?:authorize|update)\.php$ { + error_page 418 = @allowupdate; + if ( $cache_uid ) { + return 418; + } + return 404; +} + +### +### Internal location for /authorize.php and /update.php restricted access. +### +location @allowupdate { + limit_conn limreq 88; + tcp_nopush off; + keepalive_requests 0; + access_log off; + try_files $uri =404; ### check for existence of php file first + fastcgi_pass provision-php-fpm; +} + + +### +### Deny access to any not listed above php files with 404 error. +### +location ~* ^.+\.php$ { + return 404; +} + +####################################################### + +### nginx.conf site level extended vhost include end + +### nginx.conf site level basic vhost include end + +####################################################### diff --git a/src/Provision/Service/HttpService.php b/src/Provision/Service/HttpService.php index 0d4c23260..081e0f9d6 100644 --- a/src/Provision/Service/HttpService.php +++ b/src/Provision/Service/HttpService.php @@ -134,7 +134,7 @@ function verifyPlatform() { /** * Return the default restart command for this service. */ - static function default_restart_cmd() { + public static function default_restart_cmd() { return ''; }