Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Content-Type behavior #86

Merged
merged 17 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
services:
openresty:
image: openresty/openresty:1.21.4.1-0-jammy
container_name: openresty
ports:
- 8081:8081
volumes:
- ./nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf
expose:
- "8081"
node:
build:
context: ./node
container_name: node
volumes:
- ./node/web.js:/usr/src/app/web.js
environment:
NODE_ENV: production
PORT: 3000
ports:
- 3000:3000
142 changes: 142 additions & 0 deletions docker/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
worker_processes 4;

error_log stderr debug;

worker_rlimit_nofile 40000;

events {
worker_connections 16383;
multi_accept on;
use epoll;
}

http {

##
# Basic stuff
##

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 1200;
types_hash_max_size 2048;
map_hash_bucket_size 128;
server_tokens off;

# see https://stackoverflow.com/a/37656784
resolver 127.0.0.11 ipv6=off;

default_type application/octet-stream;
types {
text/plain log;
text/plain asc;
}

##
# Logging
##

log_format main '$remote_addr - [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $request_time '
'$upstream_response_time $pipe';

access_log /dev/stdout;

##
# Gzip
##

gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_types text/plain text/css application/x-javascript application/json text/xml application/xml application/xml+rss text/javascript application/rss+xml application/javascript;


##
# Timeout variables (currently disabled)
##

client_body_timeout 10m;
client_max_body_size 1024m;

large_client_header_buffers 4 16k;

# Add map for websockets
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

# Add secure header values if not set upstream, else add headers with the
# implicit default value of empty string, which is ignored by the `add_header`
# directive.
map $upstream_http_strict_transport_security $sts {
'' 'max-age=31536000';
}
map $upstream_http_x_frame_options $frame_options {
default '$upstream_http_x_frame_options';
'' 'DENY';
'ALLOWALL' '';
}
map $upstream_http_x_content_type_options $content_type_options {
'' 'nosniff';
}
map $upstream_http_x_xss_protection $xss_protection {
'' '1; mode=block';
}
map $upstream_http_content_type $default_content_type {
'' 'text/plain; charset=utf-8';
}
map $upstream_status $mapped_content_type {
default '$default_content_type';
'204' '';
'304' '';
}

include /etc/nginx/conf.d/*.conf;

server {
listen 8081;

set $backend "http://node:3000";

# proxy all traffic
location / {

root /usr/local/openresty/nginx/html;
index index.html index.htm;

##
# Security
##

add_header Strict-Transport-Security $sts always;
add_header X-Content-Type-Options $content_type_options always;
add_header X-XSS-Protection $xss_protection always;
add_header Content-Type $mapped_content_type always;

header_filter_by_lua_block {
if ngx.var.upstream_http_content_type == nil and ngx.var.mapped_content_type == "" then
ngx.log(ngx.INFO, "no Content-Type header")
end
}

proxy_buffering off;
proxy_buffer_size 16k;
proxy_buffers 4 16k;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header Proxy "";
proxy_redirect off;
proxy_pass_request_headers on;
proxy_connect_timeout 10;
proxy_read_timeout 600;
proxy_pass $backend;
}
}
}

28 changes: 28 additions & 0 deletions docker/node/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# syntax=docker/dockerfile:1

# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Dockerfile reference guide at
# https://docs.docker.com/go/dockerfile-reference/

# Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7

ARG NODE_VERSION=20.10.0

FROM node:${NODE_VERSION}-alpine

# Use production node environment by default.
ENV NODE_ENV production

WORKDIR /usr/src/app

# Run the application as a non-root user.
USER node

# Copy the rest of the source files into the image.
COPY . .

# Expose the port that the application listens on.
EXPOSE 3000

# Run the application.
CMD node web.js
25 changes: 25 additions & 0 deletions docker/node/web.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const http = require('http');
const os = require('os');
const port = process.env.PORT || 5000;

http.createServer( (req, res) => {
var url = req.url;

if (url === "/foo") {
res.writeHead(204);
res.end();
} else if (url === "/bar") {
res.writeHead(304);
res.end();
} else if (url === "/html") {
res.writeHead(200, undefined, {
'Content-Type': 'text/html'
});
res.end('<h1>foobar</h1>');
} else {
res.writeHead(200);
res.end(`Hello World from NodeJS on port ${port} from container ${os.hostname()}`);
}
}).listen(port, () => {
console.log("Listening on " + port);
});
21 changes: 19 additions & 2 deletions jobs/secureproxy/templates/config/nginx.conf.erb
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ http {
map $upstream_http_content_type $default_content_type {
'' 'text/plain; charset=utf-8';
}
map $upstream_status $mapped_content_type {
default '$default_content_type';
'204' '';
'304' '';
}

<% if p('secureproxy.csp.enable') %>
# right now, we're doing this with report-only. later, we'll drop report-only and move to enforce with report
Expand Down Expand Up @@ -179,7 +184,7 @@ http {
add_header Strict-Transport-Security $sts always;
add_header X-Content-Type-Options $content_type_options always;
add_header X-XSS-Protection $xss_protection always;
add_header Content-Type $default_content_type always;
add_header Content-Type $mapped_content_type always;

# Clear X-Frame-Options before setting so that ALLOWALL is cleared if set
more_clear_headers X-Frame-Options;
Expand All @@ -190,6 +195,12 @@ http {
more_set_headers "<%= csp_header %>: $content_security_policy";
<% end %>

header_filter_by_lua_block {
if ngx.var.upstream_http_content_type == nil and ngx.var.mapped_content_type == "" then
ngx.log(ngx.INFO, "no Content-Type header")
end
}

##
# Implement per-domain IP Whitelist
##
Expand Down Expand Up @@ -279,7 +290,13 @@ server {
add_header Strict-Transport-Security $sts always;
add_header X-Content-Type-Options $content_type_options always;
add_header X-XSS-Protection $xss_protection always;
add_header Content-Type $default_content_type always;
add_header Content-Type $mapped_content_type always;

header_filter_by_lua_block {
if ngx.var.upstream_http_content_type == nil and ngx.var.mapped_content_type == "" then
ngx.log(ngx.INFO, "no Content-Type header")
end
}

# Clear X-Frame-Options before setting so that ALLOWALL is cleared if set
more_clear_headers X-Frame-Options;
Expand Down