diff --git a/src/nginxconfig/generators/security.conf.js b/src/nginxconfig/generators/security.conf.js index 90e1029..3afd5df 100644 --- a/src/nginxconfig/generators/security.conf.js +++ b/src/nginxconfig/generators/security.conf.js @@ -1,28 +1,28 @@ import commonHsts from '../util/common_hsts'; export default (domains, global) => { - const config = {}; + const config = []; - config['# security headers'] = ''; - config['add_header X-Frame-Options'] = '"SAMEORIGIN" always'; - config['add_header X-XSS-Protection'] = '"1; mode=block" always'; - config['add_header X-Content-Type-Options'] = '"nosniff" always'; - config['add_header Referrer-Policy'] = `"${global.security.referrerPolicy.computed}" always`; + config.push(['# security headers', '']); + config.push(['add_header', 'X-Frame-Options "SAMEORIGIN" always']); + config.push(['add_header', 'X-XSS-Protection "1; mode=block" always']); + config.push(['add_header', 'X-Content-Type-Options "nosniff" always']); + config.push(['add_header', `Referrer-Policy "${global.security.referrerPolicy.computed}" always`]); if (global.security.contentSecurityPolicy.computed) - config['add_header Content-Security-Policy'] = `"${global.security.contentSecurityPolicy.computed}" always`; + config.push(['add_header', `Content-Security-Policy "${global.security.contentSecurityPolicy.computed}" always`]); // Every domain has HSTS enabled, and they all have same hstsSubdomains/hstsPreload settings if (commonHsts(domains)) { const commonHSTSSubdomains = domains.length && domains[0].https.hstsSubdomains.computed; const commonHSTSPreload = domains.length && domains[0].https.hstsPreload.computed; - config['add_header Strict-Transport-Security'] = `"max-age=31536000${commonHSTSSubdomains ? '; includeSubDomains' : ''}${commonHSTSPreload ? '; preload' : ''}" always`; + config.push(['add_header', `Strict-Transport-Security "max-age=31536000${commonHSTSSubdomains ? '; includeSubDomains' : ''}${commonHSTSPreload ? '; preload' : ''}" always`]); } - config['# . files'] = ''; - config['location ~ /\\.(?!well-known)'] = { + config.push(['# . files', '']); + config.push(['location ~ /\\.(?!well-known)', { deny: 'all', - }; + }]); // Done! return config; diff --git a/src/nginxconfig/generators/website.conf.js b/src/nginxconfig/generators/website.conf.js index e1951bc..9d9c63a 100644 --- a/src/nginxconfig/generators/website.conf.js +++ b/src/nginxconfig/generators/website.conf.js @@ -1,7 +1,15 @@ import { getSslCertificate, getSslCertificateKey } from '../util/get_ssl_certificate'; +import { getAccessLogDomainPath, getErrorLogDomainPath } from '../util/get_log_paths'; +import { extensions, gzipTypes } from '../util/types_extensions'; import commonHsts from '../util/common_hsts'; import securityConf from './security.conf'; -import { getAccessLogDomainPath, getErrorLogDomainPath } from '../util/get_log_paths'; +import pythonConf from './python_uwsgi.conf'; +import proxyConf from './proxy.conf'; +import phpConf from './php_fastcgi.conf'; +import generalConf from './general.conf'; +import wordPressConf from './wordpress.conf'; +import drupalConf from './drupal.conf'; +import magentoConf from './magento.conf'; export default (domain, domains, global) => { // Use kv so we can use the same key multiple times @@ -74,7 +82,7 @@ export default (domain, domains, global) => { serverConfig.push(['include', 'nginxconfig.io/security.conf']); } else { // Unified - serverConfig.push(...Object.entries(securityConf(domains, global))); + serverConfig.push(...securityConf(domains, global)); } // Access log or error log for domain @@ -97,7 +105,7 @@ export default (domain, domains, global) => { // Fallback index.html or index.php if ((domain.routing.fallbackHtml.computed || domain.routing.fallbackPhp.computed) && (!domain.reverseProxy.reverseProxy.computed || domain.reverseProxy.path.computed !== '/')) { - serverConfig.push([`# index.${domain.routing.fallbackHtml.computed ? 'html' : (domain.routing.fallbackPhp.computed ? 'php' : '' )} fallback`, '']); + serverConfig.push([`# index.${domain.routing.fallbackHtml.computed ? 'html' : (domain.routing.fallbackPhp.computed ? 'php' : '')} fallback`, '']); serverConfig.push(['location /', { try_files: `$uri $uri/ /index.${domain.routing.fallbackHtml.computed ? '.html' : (domain.routing.fallbackPhp.computed ? '.php?$query_string' : '')}`, }]); @@ -111,10 +119,148 @@ export default (domain, domains, global) => { }]); } - // TODO: Python onwards + // Python + if (domain.python.python.computed) { + if (global.tools.modularizedStructure.computed) { + // Modularized + serverConfig.push(['location /', { include: 'nginxconfig.io/python_uwsgi.conf' }]); + } else { + // Unified + serverConfig.push(['location /', pythonConf(domains, global)]); + } + + // Django + if (domain.python.djangoRules.computed) { + serverConfig.push(['# Django media', '']); + serverConfig.push(['location /media/', { alias: '$base/media/' }]); + + serverConfig.push(['# Django static', '']); + serverConfig.push(['location /static/', { alias: '$base/static/' }]); + } + } + + // Reverse proxy + if (domain.reverseProxy.reverseProxy.computed) { + const locConf = []; + locConf.push(['proxy_pass', domain.reverseProxy.proxyPass.computed]); + + if (global.tools.modularizedStructure.computed) { + // Modularized + locConf.push(['include', 'nginxconfig.io/python_uwsgi.conf']); + } else { + // Unified + locConf.push(...Object.entries(proxyConf())); + } + + serverConfig.push(['# reverse proxy', '']); + serverConfig.push([`location ${domain.reverseProxy.path.computed}`, locConf]); + } + + // PHP + if (domain.php.php.computed) { + serverConfig.push(['# handle .php', '']); + + const loc = `location ~ ${domain.routing.legacyPhpRouting.computed ? '[^/]\\.php(/|$)' : '\\.php$'}`; + if (global.tools.modularizedStructure.computed) { + // Modularized + serverConfig.push([loc, { include: 'nginxconfig.io/php_fastcgi.conf' }]); + } else { + // Unified + serverConfig.push([loc, phpConf(domains, global)]); + } + } + + // Additional config + if (global.tools.modularizedStructure.computed) { + // Modularized + serverConfig.push(['# additional config', '']); + serverConfig.push(['include', 'nginxconfig.io/general.conf']); + + if (domain.php.wordPressRules.computed) serverConfig.push(['include', 'nginxconfig.io/wordpress.conf']); + if (domain.php.drupalRules.computed) serverConfig.push(['include', 'nginxconfig.io/drupal.conf']); + if (domain.php.magentoRules.computed) serverConfig.push(['include', 'nginxconfig.io/magento.conf']); + } else { + // Unified + serverConfig.push(...Object.entries(generalConf(domains, global))); + + if (domain.php.wordPressRules.computed) serverConfig.push(...Object.entries(wordPressConf(domains, global))); + if (domain.php.drupalRules.computed) serverConfig.push(...Object.entries(drupalConf(domains, global))); + if (domain.php.magentoRules.computed) serverConfig.push(...Object.entries(magentoConf())); + } // Add the server config to the parent config now its built config.push(['server', serverConfig]); + // CDN! + if (domain.server.cdnSubdomain.computed) { + // Build the server config on its own before adding it to the parent config + const cdnConfig = []; + + if (domain.https.https.computed) { + // HTTPS + cdnConfig.push(['listen', `${ipv4Pre}443 ssl${domain.https.http2.computed ? ' http2' : ''}`]); + + // v6 + if (domain.server.listenIpv6.computed) + cdnConfig.push(['listen', + `[${domain.server.listenIpv6.computed}]:443 ssl${domain.https.http2.computed ? ' http2' : ''}`]); + } else { + // Not HTTPS + cdnConfig.push(['listen', `${ipv4Pre}80`]); + + // v6 + if (domain.server.listenIpv6.computed) + cdnConfig.push(['listen', `[${domain.server.listenIpv6.computed}]:80`]); + } + + cdnConfig.push(['server_name', `cdn.${domain.server.domain.computed}`]); + cdnConfig.push(['root', `${domain.server.path.computed}${domain.server.documentRoot.computed}`]); + + // HTTPS + if (domain.https.https.computed) { + serverConfig.push(['# SSL', '']); + serverConfig.push(['ssl_certificate', getSslCertificate(domain, global)]); + serverConfig.push(['ssl_certificate_key', getSslCertificateKey(domain, global)]); + + // Let's encrypt + if (domain.https.certType.computed === 'letsEncrypt') + serverConfig.push(['ssl_trusted_certificate', + `/etc/letsencrypt/live/${domain.server.domain.computed}/chain.pem`]); + } + + cdnConfig.push(['# disable access_log', '']); + cdnConfig.push(['access_log', 'off']); + + // Gzip + if (global.performance.gzipCompression.computed) { + cdnConfig.push(['# gzip', '']); + cdnConfig.push(['gzip', 'on']); + cdnConfig.push(['gzip_vary', 'on']); + cdnConfig.push(['gzip_proxied', 'any']); + cdnConfig.push(['gzip_comp_level', 6]); + cdnConfig.push(['gzip_types', gzipTypes]); + } + + cdnConfig.push(['# allow safe files', '']); + cdnConfig.push([ + `location ~* \\.(?:${extensions.assets}|${extensions.fonts}|${extensions.svg}|${extensions.images}|${extensions.audio}|${extensions.video}|${extensions.docs})$`, + [ + ['add_header', 'Access-Control-Allow-Origin "*"'], + ['add_header', 'Cache-Control "public"'], + ['expires', '30d'], + ], + ]); + + cdnConfig.push(['# deny everything else', '']); + cdnConfig.push(['location /', { deny: 'all' }]); + + // Add the CDN config to the parent config now its built + config.push(['# CDN', '']); + config.push(['server', cdnConfig]); + } + + // TODO: subdomain redirects + // TODO: HTTP redirect + return config; };