Permalink
Fetching contributors…
Cannot retrieve contributors at this time
322 lines (283 sloc) 11.1 KB
#
# Note: apply this file via apply_config to fill in the DNS
# resolver required for cookie-based routing to canary/test services
#
apiVersion: v1
kind: ConfigMap
metadata:
name: revproxy-nginx-conf
data:
userid.js: |
/** global supporting atob polyfill below */
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
/**
* base64 decode polyfill from
* https://github.com/davidchambers/Base64.js/blob/master/base64.js
*/
function atob(input) {
var str = String(input).replace(/[=]+$/, ''); // #31: ExtendScript bad parse of /=
if (str.length % 4 == 1) {
return input;
}
for (
// initialize result and counters
var bc = 0, bs, buffer, idx = 0, output = '';
// get next character
buffer = str.charAt(idx++);
// character found in table? initialize bit storage and add its ascii value;
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
// and if not first of each 4 characters,
// convert the first 8 bits to one ascii character
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
) {
// try to find character in table (0-63, not found => -1)
buffer = chars.indexOf(buffer);
}
return output;
}
/**
* nginscript helper for parsing user out of JWT tokens.
* We appear to have access to the 'access_token' variable
* defined in nginx.conf when this function runs via 'js_set'.
* see https://www.nginx.com/blog/introduction-nginscript/
*
* @param {*} req
* @param {*} res
*/
function userid(req, res) {
var token = req.variables["access_token"];
var user = "uid:null,unknown@unknown";
if (token) {
user = token;
var raw = atob((token.split('.')[1] || "").replace('-', '+').replace('_', '/'));
if (raw) {
try {
var data = JSON.parse(raw);
if (data) {
if (data.context && data.context.user && data.context.user.name) {
user = "uid:" + data.sub + "," + data.context.user.name;
}
}
} catch (err) {}
}
}
return user;
}
nginx.conf: |
user nginx;
worker_processes 4;
pid /var/run/nginx.pid;
load_module modules/ngx_http_perl_module.so;
env POD_NAMESPACE;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
port_in_redirect off;
# server_tokens off;
# For websockets
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
map $proxy_protocol_addr $initialip {
"" $http_x_forwarded_for;
default $proxy_protocol_addr;
}
map $initialip $realip {
"" $remote_addr; #if this header missing set remote_addr as real ip
default $initialip;
}
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# Note - nginscript js_set, etc get processed
# on demand: https://www.nginx.com/blog/introduction-nginscript/
#
js_include userid.js;
js_set $userid userid;
##
# Logging Settings
##
log_format aws '$realip - $userid [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
log_format json '{"gen3log": "nginx", '
'"date_access": "$time_iso8601", '
'"user_id": "$userid", '
'"request_id": "$request_id", '
'"session_id": "$session_id", '
'"visitor_id": "$visitor_id", '
'"network_client_ip": "$realip", '
'"network_bytes_write": $body_bytes_sent, '
'"http_response_time": "$request_time", '
'"http_status_code": $status, '
'"http_request": "$request_uri", '
'"http_verb": "$request_method", '
'"http_referer": "$http_referer", '
'"http_useragent": "$http_user_agent", '
'"message": "$request"}';
access_log /dev/stdout json;
error_log /dev/stderr;
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
gzip_proxied any;
gzip_types
text/css
text/javascript
text/xml
text/plain
application/javascript
application/x-javascript
application/json;
##
# Namespace
##
perl_set $namespace 'sub { return $ENV{"POD_NAMESPACE"}; }';
##
# Proxy Settings
##
# Serve internet facing http requests via this, and redirect to https
server {
listen 82 default_server proxy_protocol;
listen 83;
rewrite ^ https://$host$request_uri? permanent;
}
# Serve internet facing https requests and internal http requests here
server {
listen 81 proxy_protocol;
listen 80;
listen 443 ssl;
ssl_certificate /mnt/ssl/external.crt;
ssl_certificate_key /mnt/ssl/external.key;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
server_tokens off;
more_set_headers "Server: gen3"
more_set_headers "Strict-Transport-Security: max-age=63072000; includeSubdomains; preload";
more_set_headers "X-Frame-Options: SAMEORIGIN";
more_set_headers "X-Content-Type-Options: nosniff";
if ($http_x_forwarded_proto = "http") { return 301 https://$host$request_uri; }
#
# From https://enable-cors.org/server_nginx.html
# This overrides the individual services
#
set $allow_origin "*";
if ($http_origin) {
set $allow_origin "$http_origin";
}
more_set_headers "Access-Control-Allow-Origin: $allow_origin";
more_set_headers "Access-Control-Allow-Methods: GET, POST, OPTIONS";
#
# DO NOT DO THIS!!!
# Opens us up to CSRF requests from sites in other tabs ...
# TODO - add CORS whitelist to gitops
# https://stackoverflow.com/questions/24687313/what-exactly-does-the-access-control-allow-credentials-header-do
#
#more_set_headers "Access-Control-Allow-Credentials: true";
more_set_headers "Access-Control-Allow-Headers: DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,Cookie,X-CSRF-Token";
more_set_headers "Access-Control-Expose-Headers: Content-Length,Content-Range";
if ($request_method = 'OPTIONS') {
return 204;
}
#
# DNS resolver required to resolve dynamic hostnames, btw - kubedns may not support ipv6
# see https://www.nginx.com/blog/dns-service-discovery-nginx-plus/
# https://distinctplace.com/2017/04/19/nginx-resolver-explained/
#
resolver kube-dns.kube-system.svc.cluster.local ipv6=off;
set $access_token "";
set $csrf_check "ok-tokenauth";
#
# Note: add_header blocks are inheritted iff the current block does not call add_header:
# http://nginx.org/en/docs/http/ngx_http_headers_module.html
#
set $csrf_token "$request_id$request_length$request_time$time_iso8601";
if ($cookie_csrftoken) {
set $csrf_token "$cookie_csrftoken";
}
add_header Set-Cookie "csrftoken=$csrf_token;Path=/";
# visitor and session tracking for analytics -
# https://developers.google.com/analytics/devguides/collection/analyticsjs/cookies-user-id
#
# Simple session tracking - expire the session if not active for 20 minutes
set $session_id "$request_id";
if ($cookie_session) {
set $session_id "$cookie_session";
}
add_header Set-Cookie "session=$session_id;Path=/;Max-Age=1200;HttpOnly;Secure";
# Simple visitor tracking - immortal
set $visitor_id "$request_id";
if ($cookie_visitor) {
set $visitor_id "$cookie_visitor";
}
add_header Set-Cookie "visitor=$visitor_id;Path=/;Max-Age=36000000;HttpOnly;Secure";
if ($cookie_access_token) {
set $access_token "bearer $cookie_access_token";
# cookie auth requires csrf check
set $csrf_check "fail";
}
if ($http_authorization) {
# Authorization header is present - prefer that token over cookie token
set $access_token "$http_authorization";
}
#
# Note - need to repeat this line in location blocks that call proxy_set_header,
# as nginx proxy module inherits proxy_set_header if and only if current level does
# not set headers ... http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header
#
proxy_set_header Authorization "$access_token";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For "$realip";
proxy_set_header X-UserId "$userid";
# Can propagate this request id through downstream microservice requests for tracing
proxy_set_header X-ReqId "$request_id";
#
# Accomodate large jwt token headers
# * http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size
# * https://ma.ttias.be/nginx-proxy-upstream-sent-big-header-reading-response-header-upstream/
#
proxy_buffer_size 16k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
#
# also incoming from client:
# * https://fullvalence.com/2016/07/05/cookie-size-in-nginx/
# * https://nginx.org/en/docs/http/ngx_http_core_module.html#client_header_buffer_size
large_client_header_buffers 4 8k;
client_header_buffer_size 4k;
#
# CSRF check
# This block requires a csrftoken for all POST requests.
#
if ($cookie_csrftoken = $http_x_csrf_token) {
# this will fail further below if cookie_csrftoken is empty
set $csrf_check "ok-$cookie_csrftoken";
}
if ($request_method != "POST") {
set $csrf_check "ok-$request_method";
}
if ($cookie_access_token = "") {
# do this again here b/c empty cookie_csrftoken == empty http_x_csrf_token - ugh
set $csrf_check "ok-tokenauth";
}
include /etc/nginx/gen3.conf/*.conf;
location @error401 {
return 302 $scheme://$host/login;
}
}
}