From 8fe6a2795f0c6f9107662ef9e5a43c6a3c8c066c Mon Sep 17 00:00:00 2001 From: Rainer Killinger Date: Wed, 27 Feb 2019 16:28:50 +0000 Subject: [PATCH] fix: increase nginx transport security --- Dockerfile | 5 ++++- README.md | 2 ++ config/default.ts | 7 ++++++- nginx.conf | 5 +---- src/common.ts | 36 +++++++++++++++++++++++++++++++++++- src/main.ts | 33 +++++++++++++++++++++++++++------ 6 files changed, 75 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index ea4e57c3..09fe0315 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,10 @@ ADD . /app WORKDIR /app -RUN apk add --update nginx && \ +RUN apk update && \ + apk upgrade && \ + apk add openssl && \ + apk add nginx && \ rm -rf /var/cache/apk/* && \ mv /app/nginx.conf /etc/nginx/ diff --git a/README.md b/README.md index 3144a258..2f5fdc41 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,8 @@ docker run --rm --net="host" \ -v /var/run/docker.sock:/var/run/docker.sock \ -v :/etc/nginx/certs/ssl.crt \ -v :/etc/nginx/certs/ssl.key \ + -v :/etc/nginx/certs/chain.crt \ + -v :/etc/nginx/certs/dhparam.pem \ gitlab-registry.tubit.tu-berlin.de/stapps/proxy/master ``` diff --git a/config/default.ts b/config/default.ts index e27aa9f5..8c9b6d72 100644 --- a/config/default.ts +++ b/config/default.ts @@ -20,7 +20,12 @@ const config: ConfigFile = { hiddenRoutes: ['/bulk'], outdatedVersions: ['0\\.8\\.\\d+', '0\\.5\\.\\d+', '0\\.6\\.\\d+', '0\\.7\\.\\d+'], output: '/etc/nginx/conf.d/default.conf', - sslFiles: ['/etc/nginx/certs/ssl.crt', '/etc/nginx/certs/ssl.key'], + sslFilePaths: { + certificate: '/etc/nginx/certs/ssl.crt', + certificateChain: '/etc/nginx/certs/chain.crt', + certificateKey: '/etc/nginx/certs/ssl.key', + dhparam: '/etc/nginx/certs/dhparam.pem', + }, visibleRoutes: ['/search', '/search/multi', '/', '/availabilityCreativework', '/feedback'], }; diff --git a/nginx.conf b/nginx.conf index a15a4195..9d10a339 100644 --- a/nginx.conf +++ b/nginx.conf @@ -8,7 +8,6 @@ events { worker_connections 1024; } - http { include mime.types; default_type application/octet-stream; @@ -20,10 +19,8 @@ http { sendfile on; #tcp_nopush on; - + gzip on; keepalive_timeout 65; - - gzip on; include /etc/nginx/conf.d/*; } diff --git a/src/common.ts b/src/common.ts index 16d7c2fa..952e2ac2 100644 --- a/src/common.ts +++ b/src/common.ts @@ -19,6 +19,16 @@ import {SMTP} from '@openstapps/logger/lib/SMTP'; // use SMTP as a default monitoring system for logger.error(); export const logger = new Logger(SMTP.getInstance()); +/** + * A representation of the file paths of the needed ssl certificates + */ +export interface SSLFilePaths { + certificate: string; + certificateChain: string; + certificateKey: string; + dhparam: string; +} + /** * A representation of the config file */ @@ -27,7 +37,7 @@ export interface ConfigFile { hiddenRoutes: string[]; outdatedVersions: string[]; output: string; - sslFiles: string[]; + sslFilePaths: SSLFilePaths; visibleRoutes: string[]; } @@ -41,3 +51,27 @@ export interface TemplateView { staticRoute: string; visibleRoutes: string; } + +/** + * Nginx protocol parameters to harden serverside settings + */ +export const protocolHardeningParameters: string = ` +add_header Strict-Transport-Security "max-age=63072000; includeSubDomains;"; +add_header X-Frame-Options DENY; +add_header X-Content-Type-Options nosniff; +add_header X-XSS-Protection "1; mode=block";`; + +// tslint:disable:max-line-length +/** + * Nginx ssl parameters to harden serverside settings + */ +export const sslHardeningParameters: string = ` +ssl_protocols TLSv1.2 TLSv1.3; +ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; +ssl_prefer_server_ciphers on; +ssl_ecdh_curve X25519:secp384r1; +ssl_session_timeout 10m; +ssl_session_cache shared:SSL:10m; +ssl_session_tickets off; +ssl_stapling on; +ssl_stapling_verify on;`; diff --git a/src/main.ts b/src/main.ts index 3e8ae9e4..93e44d36 100644 --- a/src/main.ts +++ b/src/main.ts @@ -18,7 +18,14 @@ import * as Dockerode from 'dockerode'; import {existsSync, readFile} from 'fs-extra'; import {render} from 'mustache'; import {join} from 'path'; -import {ConfigFile, logger, TemplateView} from './common'; +import { + ConfigFile, + logger, + protocolHardeningParameters, + SSLFilePaths, + sslHardeningParameters, + TemplateView, +} from './common'; const configFile: ConfigFile = config.util.toObject(); @@ -123,7 +130,7 @@ export function generateUpstreamMap( * @param sslFiles * @returns {string} */ -function generateListener(sslFiles: string[]) { +function generateListener(sslFilePaths: SSLFilePaths) { function isSSLCert(path: string) { return existsSync(path) && /.*\.crt$/.test(path); @@ -133,17 +140,31 @@ function generateListener(sslFiles: string[]) { return existsSync(path) && /.*\.key$/.test(path); } + function isPEMFile(path: string) { + return existsSync(path) && /.*\.pem$/.test(path); + } + let listener = ''; - if (Array.isArray(sslFiles) && sslFiles.length === 2 && sslFiles.some(isSSLCert) && sslFiles.some(isSSLKey)) { + if (typeof sslFilePaths !== 'undefined' && + typeof sslFilePaths.certificate === 'string' && isSSLCert(sslFilePaths.certificate) && + typeof sslFilePaths.certificateChain === 'string' && isSSLCert(sslFilePaths.certificate) && + typeof sslFilePaths.certificateKey === 'string' && isSSLKey(sslFilePaths.certificate) && + typeof sslFilePaths.dhparam === 'string' && isPEMFile(sslFilePaths.dhparam) + ) { // https listener listener = 'listen 443 ssl default_server;\n' + - `ssl_certificate ${sslFiles.find(isSSLCert)};\n` + - `ssl_certificate_key ${sslFiles.find(isSSLKey)};\n`; + `ssl_certificate ${sslFilePaths.certificate};\n` + + `ssl_certificate_key ${sslFilePaths.certificateKey};\n` + + `ssl_trusted_certificate ${sslFilePaths.certificateChain};\n` + + `ssl_dhparam ${sslFilePaths.dhparam};\n` + + `${sslHardeningParameters}`; } else { // default http listener listener = 'listen 80 default_server;'; + logger.warn('Https usage is not setup properly, falling back to http!'); } + listener = `${listener}\n${protocolHardeningParameters}\n`; return listener; } @@ -183,7 +204,7 @@ export async function getTemplateView(containers: Dockerode.ContainerInfo[]): Pr return { dockerVersionMap: generateUpstreamMap(configFile.activeVersions, configFile.outdatedVersions, containers), hiddenRoutes: (await Promise.all(hiddenRoutesPromises)).join(''), - listener: generateListener(configFile.sslFiles), + listener: generateListener(configFile.sslFilePaths), staticRoute: await renderTemplate(join('fixtures', 'staticRoute.template'), {cors}), visibleRoutes: (await Promise.all(visibleRoutesPromises)).join(''), };