From 05efcfc3a32621796c3f8f107e01f9101f1a7794 Mon Sep 17 00:00:00 2001 From: Lennart Krauch Date: Thu, 8 Aug 2024 15:19:47 +0200 Subject: [PATCH 1/7] feat: nginx sec headers Signed-off-by: Lennart Krauch --- nginx.conf | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/nginx.conf b/nginx.conf index 73dcd7248c..4415a5ea78 100644 --- a/nginx.conf +++ b/nginx.conf @@ -5,7 +5,7 @@ server { server_name beta.scrumlr.io scrumler.io scrumlr.de; location / { - return 301 https://scrumlr.io$request_uri; + return 301 https://scrumlr.io$request_uri; } } @@ -20,28 +20,38 @@ server { gzip on; gzip_vary on; gzip_comp_level 6; - gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; + gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; gzip_proxied no-cache no-store private expired auth; gzip_min_length 1024; gzip_disable "MSIE [1-6]\."; + # Security headers + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + add_header Content-Security-Policy "default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline'; frame-ancestors 'self';" always; + add_header Permissions-Policy "interest-cohort=()" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Frame-Options "DENY" always; + add_header Expect-CT "max-age=86400, enforce" always; + location / { - try_files $uri $uri/ /index.html; - add_header Cache-Control "no-store, no-cache, must-revalidate"; + try_files $uri $uri/ /index.html; + add_header Cache-Control "no-store, no-cache, must-revalidate"; } location /index.html { - # Application specific feature toggles - add_header Set-Cookie "scrumlr__show-legal-documents=${SCRUMLR_SHOW_LEGAL_DOCUMENTS};Path=/;Max-Age=3600"; - add_header Set-Cookie "scrumlr__server-url=${SCRUMLR_SERVER_URL};Path=/;Max-Age=3600"; - add_header Set-Cookie "scrumlr__websocket-url=${SCRUMLR_WEBSOCKET_URL};Path=/;Max-Age=3600"; - add_header Set-Cookie "scrumlr__analytics_data_domain=${SCRUMLR_ANALYTICS_DATA_DOMAIN};Path=/;Max-Age=3600"; - add_header Set-Cookie "scrumlr__analytics_src=${SCRUMLR_ANALYTICS_SRC};Path=/;Max-Age=3600"; - - # Disable caching for index.html - add_header Cache-Control "no-store, no-cache, must-revalidate"; - add_header Pragma no-cache; - expires 0; + # Application specific feature toggles + add_header Set-Cookie "scrumlr__show-legal-documents=${SCRUMLR_SHOW_LEGAL_DOCUMENTS};Path=/;Max-Age=3600"; + add_header Set-Cookie "scrumlr__server-url=${SCRUMLR_SERVER_URL};Path=/;Max-Age=3600"; + add_header Set-Cookie "scrumlr__websocket-url=${SCRUMLR_WEBSOCKET_URL};Path=/;Max-Age=3600"; + add_header Set-Cookie "scrumlr__analytics_data_domain=${SCRUMLR_ANALYTICS_DATA_DOMAIN};Path=/;Max-Age=3600"; + add_header Set-Cookie "scrumlr__analytics_src=${SCRUMLR_ANALYTICS_SRC};Path=/;Max-Age=3600"; + + # Disable caching for index.html + add_header Cache-Control "no-store, no-cache, must-revalidate"; + add_header Pragma no-cache; + expires 0; } location ~* \.(js|json)$ { @@ -57,7 +67,10 @@ server { } location ~* \.(ico|mp3)$ { - # Cache .mp3 and .ico files for 365 days - add_header Cache-Control "max-age=36792000, public"; + # Cache .mp3 and .ico files for 365 days + add_header Cache-Control "max-age=36792000, public"; } } + +# Hide Nginx version number +server_tokens off; From b9a2f897a325b84399b838e8075915f8794439ca Mon Sep 17 00:00:00 2001 From: Lennart Krauch Date: Thu, 8 Aug 2024 15:20:55 +0200 Subject: [PATCH 2/7] feat: nginx sec headers Signed-off-by: Lennart Krauch --- nginx.conf | 127 +++++++++++++++++++++++++++-------------------------- 1 file changed, 64 insertions(+), 63 deletions(-) diff --git a/nginx.conf b/nginx.conf index 4415a5ea78..0a772aec63 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,76 +1,77 @@ server { - # Redirect alternative domains to scrumlr.io - listen ${SCRUMLR_LISTEN_PORT}; - listen [::]:${SCRUMLR_LISTEN_PORT}; - server_name beta.scrumlr.io scrumler.io scrumlr.de; + # Redirect alternative domains to scrumlr.io + listen $ { + SCRUMLR_LISTEN_POR + } + ; + listen [::]:$ { + SCRUMLR_LISTEN_POR + } + ; + server_name beta.scrumlr.io scrumler.io scrumlr.de; - location / { - return 301 https://scrumlr.io$request_uri; - } + location / { + return 301 https://scrumlr.io$request_uri; + } } server { - listen ${SCRUMLR_LISTEN_PORT} default_server; - listen [::]:${SCRUMLR_LISTEN_PORT} default_server; - server_name _; + listen $ { + SCRUMLR_LISTEN_POR + } + default_server; + listen [::]:$ { + SCRUMLR_LISTEN_POR + } + default_server; + server_name _; - root /usr/share/nginx/html; - index index.html index.htm; + root /usr/share/nginx/html; + index index.html index.htm; - gzip on; - gzip_vary on; - gzip_comp_level 6; - gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; - gzip_proxied no-cache no-store private expired auth; - gzip_min_length 1024; - gzip_disable "MSIE [1-6]\."; + gzip on; + gzip_vary on; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; + gzip_proxied no-cache no-store private expired auth; + gzip_min_length 1024; + gzip_disable "MSIE [1-6]\."; - # Security headers - add_header X-XSS-Protection "1; mode=block" always; - add_header X-Content-Type-Options "nosniff" always; - add_header Referrer-Policy "no-referrer-when-downgrade" always; - add_header Content-Security-Policy "default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline'; frame-ancestors 'self';" always; - add_header Permissions-Policy "interest-cohort=()" always; - add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; - add_header X-Frame-Options "DENY" always; - add_header Expect-CT "max-age=86400, enforce" always; + # Security headers + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + add_header Content-Security-Policy "default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline'; frame-ancestors 'self';" always; + add_header Permissions-Policy "interest-cohort=()" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Frame-Options "DENY" always; + add_header Expect-CT "max-age=86400, enforce" always; - location / { - try_files $uri $uri/ /index.html; - add_header Cache-Control "no-store, no-cache, must-revalidate"; - } + location / { + try_files $uri $uri/ /index.html; + add_header Cache-Control "no-store, no-cache, must-revalidate"; + } - location /index.html { - # Application specific feature toggles - add_header Set-Cookie "scrumlr__show-legal-documents=${SCRUMLR_SHOW_LEGAL_DOCUMENTS};Path=/;Max-Age=3600"; - add_header Set-Cookie "scrumlr__server-url=${SCRUMLR_SERVER_URL};Path=/;Max-Age=3600"; - add_header Set-Cookie "scrumlr__websocket-url=${SCRUMLR_WEBSOCKET_URL};Path=/;Max-Age=3600"; - add_header Set-Cookie "scrumlr__analytics_data_domain=${SCRUMLR_ANALYTICS_DATA_DOMAIN};Path=/;Max-Age=3600"; - add_header Set-Cookie "scrumlr__analytics_src=${SCRUMLR_ANALYTICS_SRC};Path=/;Max-Age=3600"; + location /index.html { + # Application specific feature toggles + add_header Set-Cookie "scrumlr__show-legal-documents=${SCRUMLR_SHOW_LEGAL_DOCUMENTS};Path=/;Max-Age=3600"; + add_header Set-Cookie "scrumlr__server-url=${SCRUMLR_SERVER_URL};Path=/;Max-Age=3600"; + add_header Set-Cookie "scrumlr__websocket-url=${SCRUMLR_WEBSOCKET_URL};Path=/;Max-Age=3600"; + add_header Set-Cookie "scrumlr__analytics_data_domain=${SCRUMLR_ANALYTICS_DATA_DOMAIN};Path=/;Max-Age=3600"; + add_header Set-Cookie "scrumlr__analytics_src=${SCRUMLR_ANALYTICS_SRC};Path=/;Max-Age=3600"; - # Disable caching for index.html - add_header Cache-Control "no-store, no-cache, must-revalidate"; - add_header Pragma no-cache; - expires 0; - } + # Disable caching for index.html + add_header Cache-Control "no-store, no-cache, must-revalidate"; + add_header Pragma no-cache; + expires 0; + } - location ~* \.(js|json)$ { - # Cache JS and JSON files for 3 days - expires 3d; - add_header Cache-Control "public, must-revalidate"; - } + location ~* \.(js|json)$ { + # Cache JS and JSON files for 3 days + expires 3d; + add_header Cache-Control "public, must-revalidate"; + } - location ~* \.(jpg|jpeg|gif|png|svg|css)$ { - # Cache other media files for 14 days - expires 14d; - add_header Cache-Control "public"; - } - - location ~* \.(ico|mp3)$ { - # Cache .mp3 and .ico files for 365 days - add_header Cache-Control "max-age=36792000, public"; - } -} - -# Hide Nginx version number -server_tokens off; + location ~* \.(jpg|jpeg|gif|png|svg|css)$ { + # Cache other media files for 14 days + expires 14d; From 15483205a299873cb3f61da4f90c48c96bcf0ef4 Mon Sep 17 00:00:00 2001 From: Lennart Krauch Date: Thu, 8 Aug 2024 15:22:48 +0200 Subject: [PATCH 3/7] rm hsts Signed-off-by: Lennart Krauch --- nginx.conf | 1 - 1 file changed, 1 deletion(-) diff --git a/nginx.conf b/nginx.conf index 0a772aec63..a6485a9a8d 100644 --- a/nginx.conf +++ b/nginx.conf @@ -43,7 +43,6 @@ server { add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Content-Security-Policy "default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline'; frame-ancestors 'self';" always; add_header Permissions-Policy "interest-cohort=()" always; - add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Frame-Options "DENY" always; add_header Expect-CT "max-age=86400, enforce" always; From 8b7abf416004d142f8e72dd2269ff3fea5b121b0 Mon Sep 17 00:00:00 2001 From: Lennart Krauch Date: Thu, 8 Aug 2024 16:06:45 +0200 Subject: [PATCH 4/7] chore: Update docker-compose.yml and nginx.conf paths --- deployment/docker/docker-compose.yml | 4 +- nginx.conf | 125 +++++++++++++-------------- 2 files changed, 64 insertions(+), 65 deletions(-) diff --git a/deployment/docker/docker-compose.yml b/deployment/docker/docker-compose.yml index 3e63c554d5..27f3da84aa 100644 --- a/deployment/docker/docker-compose.yml +++ b/deployment/docker/docker-compose.yml @@ -4,7 +4,7 @@ services: scrumlr-backend: restart: always build: - context: ../server/src/ + context: ../../server/src/ dockerfile: Dockerfile command: - "/app/main" @@ -42,7 +42,7 @@ services: scrumlr-frontend: restart: always build: - context: ../. + context: ../../. dockerfile: Dockerfile environment: SCRUMLR_SERVER_URL: "${SCRUMLR_SERVER_URL}" diff --git a/nginx.conf b/nginx.conf index a6485a9a8d..cfe4752872 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,76 +1,75 @@ server { - # Redirect alternative domains to scrumlr.io - listen $ { - SCRUMLR_LISTEN_POR - } - ; - listen [::]:$ { - SCRUMLR_LISTEN_POR - } - ; - server_name beta.scrumlr.io scrumler.io scrumlr.de; + # Redirect alternative domains to scrumlr.io + listen ${SCRUMLR_LISTEN_PORT}; + listen [::]:${SCRUMLR_LISTEN_PORT}; + server_name beta.scrumlr.io scrumler.io scrumlr.de; - location / { - return 301 https://scrumlr.io$request_uri; - } + location / { + return 301 https://scrumlr.io$request_uri; + } } server { - listen $ { - SCRUMLR_LISTEN_POR - } - default_server; - listen [::]:$ { - SCRUMLR_LISTEN_POR - } - default_server; - server_name _; + listen ${SCRUMLR_LISTEN_PORT} default_server; + listen [::]:${SCRUMLR_LISTEN_PORT} default_server; + server_name _; - root /usr/share/nginx/html; - index index.html index.htm; + root /usr/share/nginx/html; + index index.html index.htm; - gzip on; - gzip_vary on; - gzip_comp_level 6; - gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; - gzip_proxied no-cache no-store private expired auth; - gzip_min_length 1024; - gzip_disable "MSIE [1-6]\."; + gzip on; + gzip_vary on; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; + gzip_proxied no-cache no-store private expired auth; + gzip_min_length 1024; + gzip_disable "MSIE [1-6]\."; - # Security headers - add_header X-XSS-Protection "1; mode=block" always; - add_header X-Content-Type-Options "nosniff" always; - add_header Referrer-Policy "no-referrer-when-downgrade" always; - add_header Content-Security-Policy "default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline'; frame-ancestors 'self';" always; - add_header Permissions-Policy "interest-cohort=()" always; - add_header X-Frame-Options "DENY" always; - add_header Expect-CT "max-age=86400, enforce" always; + # Security headers + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + add_header Content-Security-Policy "default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline'; frame-ancestors 'self';" always; + add_header Permissions-Policy "interest-cohort=()" always; + add_header X-Frame-Options "DENY" always; + add_header Expect-CT "max-age=86400, enforce" always; - location / { - try_files $uri $uri/ /index.html; - add_header Cache-Control "no-store, no-cache, must-revalidate"; - } + location / { + try_files $uri $uri/ /index.html; + add_header Cache-Control "no-store, no-cache, must-revalidate"; + } - location /index.html { - # Application specific feature toggles - add_header Set-Cookie "scrumlr__show-legal-documents=${SCRUMLR_SHOW_LEGAL_DOCUMENTS};Path=/;Max-Age=3600"; - add_header Set-Cookie "scrumlr__server-url=${SCRUMLR_SERVER_URL};Path=/;Max-Age=3600"; - add_header Set-Cookie "scrumlr__websocket-url=${SCRUMLR_WEBSOCKET_URL};Path=/;Max-Age=3600"; - add_header Set-Cookie "scrumlr__analytics_data_domain=${SCRUMLR_ANALYTICS_DATA_DOMAIN};Path=/;Max-Age=3600"; - add_header Set-Cookie "scrumlr__analytics_src=${SCRUMLR_ANALYTICS_SRC};Path=/;Max-Age=3600"; + location /index.html { + # Application specific feature toggles + add_header Set-Cookie "scrumlr__show-legal-documents=${SCRUMLR_SHOW_LEGAL_DOCUMENTS};Path=/;Max-Age=3600"; + add_header Set-Cookie "scrumlr__server-url=${SCRUMLR_SERVER_URL};Path=/;Max-Age=3600"; + add_header Set-Cookie "scrumlr__websocket-url=${SCRUMLR_WEBSOCKET_URL};Path=/;Max-Age=3600"; + add_header Set-Cookie "scrumlr__analytics_data_domain=${SCRUMLR_ANALYTICS_DATA_DOMAIN};Path=/;Max-Age=3600"; + add_header Set-Cookie "scrumlr__analytics_src=${SCRUMLR_ANALYTICS_SRC};Path=/;Max-Age=3600"; - # Disable caching for index.html - add_header Cache-Control "no-store, no-cache, must-revalidate"; - add_header Pragma no-cache; - expires 0; - } + # Disable caching for index.html + add_header Cache-Control "no-store, no-cache, must-revalidate"; + add_header Pragma no-cache; + expires 0; + } - location ~* \.(js|json)$ { - # Cache JS and JSON files for 3 days - expires 3d; - add_header Cache-Control "public, must-revalidate"; - } + location ~* \.(js|json)$ { + # Cache JS and JSON files for 3 days + expires 3d; + add_header Cache-Control "public, must-revalidate"; + } - location ~* \.(jpg|jpeg|gif|png|svg|css)$ { - # Cache other media files for 14 days - expires 14d; + location ~* \.(jpg|jpeg|gif|png|svg|css)$ { + # Cache other media files for 14 days + expires 14d; + add_header Cache-Control "public, must-revalidate"; + } + + location ~* \.(ico|mp3)$ { + # Cache .mp3 and .ico files for 365 days + add_header Cache-Control "max-age=36792000, public"; + } +} + +# Hide Nginx version number +server_tokens off; From 31b829dec8717c31a62a17afb6fdd503333e2a10 Mon Sep 17 00:00:00 2001 From: Lennart Krauch Date: Thu, 8 Aug 2024 16:32:33 +0200 Subject: [PATCH 5/7] chore: Include security headers in nginx configuration --- Dockerfile | 1 + nginx.conf | 20 ++++++++++++-------- security-headers.conf | 8 ++++++++ 3 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 security-headers.conf diff --git a/Dockerfile b/Dockerfile index c77b92834a..83114e44bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,5 +35,6 @@ ENV SCRUMLR_ANALYTICS_DATA_DOMAIN='' ENV SCRUMLR_ANALYTICS_SRC='' COPY ./nginx.conf /etc/nginx/templates/scrumlr.io.conf.template +COPY ./security-headers.conf /etc/nginx/conf.d/security-headers.conf COPY --from=build-stage /usr/src/app/build /usr/share/nginx/html RUN rm /etc/nginx/conf.d/default.conf diff --git a/nginx.conf b/nginx.conf index cfe4752872..f1f784fd79 100644 --- a/nginx.conf +++ b/nginx.conf @@ -25,18 +25,14 @@ server { gzip_min_length 1024; gzip_disable "MSIE [1-6]\."; - # Security headers - add_header X-XSS-Protection "1; mode=block" always; - add_header X-Content-Type-Options "nosniff" always; - add_header Referrer-Policy "no-referrer-when-downgrade" always; - add_header Content-Security-Policy "default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline'; frame-ancestors 'self';" always; - add_header Permissions-Policy "interest-cohort=()" always; - add_header X-Frame-Options "DENY" always; - add_header Expect-CT "max-age=86400, enforce" always; + # Include security headers + include /etc/nginx/conf.d/security-headers.conf; location / { try_files $uri $uri/ /index.html; add_header Cache-Control "no-store, no-cache, must-revalidate"; + # Include security headers + include /etc/nginx/conf.d/security-headers.conf; } location /index.html { @@ -51,23 +47,31 @@ server { add_header Cache-Control "no-store, no-cache, must-revalidate"; add_header Pragma no-cache; expires 0; + # Include security headers + include /etc/nginx/conf.d/security-headers.conf; } location ~* \.(js|json)$ { # Cache JS and JSON files for 3 days expires 3d; add_header Cache-Control "public, must-revalidate"; + # Include security headers + include /etc/nginx/conf.d/security-headers.conf; } location ~* \.(jpg|jpeg|gif|png|svg|css)$ { # Cache other media files for 14 days expires 14d; add_header Cache-Control "public, must-revalidate"; + # Include security headers + include /etc/nginx/conf.d/security-headers.conf; } location ~* \.(ico|mp3)$ { # Cache .mp3 and .ico files for 365 days add_header Cache-Control "max-age=36792000, public"; + # Include security headers + include /etc/nginx/conf.d/security-headers.conf; } } diff --git a/security-headers.conf b/security-headers.conf new file mode 100644 index 0000000000..fd32b5744a --- /dev/null +++ b/security-headers.conf @@ -0,0 +1,8 @@ +# Security headers +add_header X-XSS-Protection "1; mode=block" always; +add_header X-Content-Type-Options "nosniff" always; +add_header Referrer-Policy "no-referrer-when-downgrade" always; +add_header Content-Security-Policy "default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline'; frame-ancestors 'self';" always; +add_header Permissions-Policy "interest-cohort=()" always; +add_header X-Frame-Options "DENY" always; +add_header Expect-CT "max-age=86400, enforce" always; From aafc2b667b6d1d3a34095c0fb1dd0d5474800ad7 Mon Sep 17 00:00:00 2001 From: Lennart Krauch Date: Thu, 8 Aug 2024 16:48:41 +0200 Subject: [PATCH 6/7] chore: Include security headers in nginx configuration --- nginx.conf | 6 ------ 1 file changed, 6 deletions(-) diff --git a/nginx.conf b/nginx.conf index f1f784fd79..fcbe56099b 100644 --- a/nginx.conf +++ b/nginx.conf @@ -25,13 +25,11 @@ server { gzip_min_length 1024; gzip_disable "MSIE [1-6]\."; - # Include security headers include /etc/nginx/conf.d/security-headers.conf; location / { try_files $uri $uri/ /index.html; add_header Cache-Control "no-store, no-cache, must-revalidate"; - # Include security headers include /etc/nginx/conf.d/security-headers.conf; } @@ -47,7 +45,6 @@ server { add_header Cache-Control "no-store, no-cache, must-revalidate"; add_header Pragma no-cache; expires 0; - # Include security headers include /etc/nginx/conf.d/security-headers.conf; } @@ -55,7 +52,6 @@ server { # Cache JS and JSON files for 3 days expires 3d; add_header Cache-Control "public, must-revalidate"; - # Include security headers include /etc/nginx/conf.d/security-headers.conf; } @@ -63,14 +59,12 @@ server { # Cache other media files for 14 days expires 14d; add_header Cache-Control "public, must-revalidate"; - # Include security headers include /etc/nginx/conf.d/security-headers.conf; } location ~* \.(ico|mp3)$ { # Cache .mp3 and .ico files for 365 days add_header Cache-Control "max-age=36792000, public"; - # Include security headers include /etc/nginx/conf.d/security-headers.conf; } } From 48479bd061b05b116f3b8bc9de221f364095eedc Mon Sep 17 00:00:00 2001 From: Lennart Krauch Date: Thu, 8 Aug 2024 16:52:26 +0200 Subject: [PATCH 7/7] rm unused code --- nginx.conf | 3 --- 1 file changed, 3 deletions(-) diff --git a/nginx.conf b/nginx.conf index fcbe56099b..7ad0b13156 100644 --- a/nginx.conf +++ b/nginx.conf @@ -68,6 +68,3 @@ server { include /etc/nginx/conf.d/security-headers.conf; } } - -# Hide Nginx version number -server_tokens off;