diff --git a/CHANGES b/CHANGES index cb671e437..721a9618e 100644 --- a/CHANGES +++ b/CHANGES @@ -2176,4 +2176,5 @@ * apptask: backup/restore tasks now use the backup memory limit configuration * eventlog: add logout event * mailbox: include alias in mailbox search +* proxyAuth: add path exclusion diff --git a/src/nginxconfig.ejs b/src/nginxconfig.ejs index 098db3ae6..8afd78c5a 100644 --- a/src/nginxconfig.ejs +++ b/src/nginxconfig.ejs @@ -175,7 +175,75 @@ server { <% if (proxyAuth.enabled) { %> proxy_set_header X-App-ID "<%= proxyAuth.id %>"; +<% } %> + # increase the proxy buffer sizes to not run into buffer issues (http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffers) + proxy_buffer_size 128k; + proxy_buffers 4 256k; + proxy_busy_buffers_size 256k; + + # No buffering to temp files, it fails for large downloads + proxy_max_temp_file_size 0; + + # Disable check to allow unlimited body sizes. this allows apps to accept whatever size they want + client_max_body_size 0; + +<% if (robotsTxtQuoted) { %> + location = /robots.txt { + return 200 <%- robotsTxtQuoted %>; + } +<% } %> + +<% if ( endpoint === 'admin' || endpoint === 'setup' ) { %> + location /api/ { + proxy_pass http://127.0.0.1:3000; + client_max_body_size 1m; + } + + location ~ ^/api/v1/(developer|session)/login$ { + proxy_pass http://127.0.0.1:3000; + client_max_body_size 1m; + limit_req zone=admin_login burst=5; + } + + # the read timeout is between successive reads and not the whole connection + location ~ ^/api/v1/apps/.*/exec$ { + proxy_pass http://127.0.0.1:3000; + proxy_read_timeout 30m; + } + + location ~ ^/api/v1/apps/.*/upload$ { + proxy_pass http://127.0.0.1:3000; + client_max_body_size 0; + } + + location ~ ^/api/v1/apps/.*/files/ { + proxy_pass http://127.0.0.1:3000; + client_max_body_size 0; + } + + location ~ ^/api/v1/volumes/.*/files/ { + proxy_pass http://127.0.0.1:3000; + client_max_body_size 0; + } + + # graphite paths (uncomment block below and visit /graphite-web/dashboard) + # remember to comment out the CSP policy as well to access the graphite dashboard + # location ~ ^/graphite-web/ { + # proxy_pass http://127.0.0.1:8417; + # client_max_body_size 1m; + # } + + location / { + root <%= sourceDir %>/dashboard/dist; + index index.html index.htm; + } +<% } else if ( endpoint === 'app' ) { %> + location = /appstatus.html { + root /home/yellowtent/box/dashboard/dist; + } + + <% if (proxyAuth.enabled) { %> location = /proxy-auth { internal; proxy_pass http://127.0.0.1:3001/auth; @@ -192,76 +260,8 @@ server { location @proxy-auth-login { return 302 /login?redirect=$request_uri; } -<% } %> - location / { - # increase the proxy buffer sizes to not run into buffer issues (http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffers) - proxy_buffer_size 128k; - proxy_buffers 4 256k; - proxy_busy_buffers_size 256k; - - # No buffering to temp files, it fails for large downloads - proxy_max_temp_file_size 0; - - # Disable check to allow unlimited body sizes. this allows apps to accept whatever size they want - client_max_body_size 0; - -<% if (robotsTxtQuoted) { %> - location = /robots.txt { - return 200 <%- robotsTxtQuoted %>; - } -<% } %> - -<% if ( endpoint === 'admin' || endpoint === 'setup' ) { %> - location /api/ { - proxy_pass http://127.0.0.1:3000; - client_max_body_size 1m; - } - - location ~ ^/api/v1/(developer|session)/login$ { - proxy_pass http://127.0.0.1:3000; - client_max_body_size 1m; - limit_req zone=admin_login burst=5; - } - - # the read timeout is between successive reads and not the whole connection - location ~ ^/api/v1/apps/.*/exec$ { - proxy_pass http://127.0.0.1:3000; - proxy_read_timeout 30m; - } - - location ~ ^/api/v1/apps/.*/upload$ { - proxy_pass http://127.0.0.1:3000; - client_max_body_size 0; - } - - location ~ ^/api/v1/apps/.*/files/ { - proxy_pass http://127.0.0.1:3000; - client_max_body_size 0; - } - - location ~ ^/api/v1/volumes/.*/files/ { - proxy_pass http://127.0.0.1:3000; - client_max_body_size 0; - } - - # graphite paths (uncomment block below and visit /graphite-web/dashboard) - # remember to comment out the CSP policy as well to access the graphite dashboard - # location ~ ^/graphite-web/ { - # proxy_pass http://127.0.0.1:8417; - # client_max_body_size 1m; - # } - - location / { - root <%= sourceDir %>/dashboard/dist; - index index.html index.htm; - } -<% } else if ( endpoint === 'app' ) { %> - location = /appstatus.html { - } - - <% if (proxyAuth.enabled) { %> - location "<%= proxyAuth.path %>" { + location <%= proxyAuth.location %> { auth_request /proxy-auth; auth_request_set $auth_cookie $upstream_http_set_cookie; error_page 401 = @proxy-auth-login; @@ -271,28 +271,29 @@ server { <% } %> <% Object.keys(httpPaths).forEach(function (path) { -%> - location "<%= path %>" { - # the trailing / will replace part of the original URI matched by the location. - proxy_pass http://<%= ip %>:<%= httpPaths[path] %>/; - } + location "<%= path %>" { + # the trailing / will replace part of the original URI matched by the location. + proxy_pass http://<%= ip %>:<%= httpPaths[path] %>/; + } <% }); %> + location / { proxy_pass http://<%= ip %>:<%= port %>; + } <% } else if ( endpoint === 'redirect' ) { %> # redirect everything to the app. this is temporary because there is no way # to clear a permanent redirect on the browser return 302 https://<%= redirectTo %>$request_uri; <% } else if ( endpoint === 'ip' ) { %> - location /notfound.html { - root <%= sourceDir %>/dashboard/dist; - try_files /notfound.html =404; - internal; - } - - location / { - root /home/yellowtent/boxdata; - try_files /custom_pages/notfound.html /notfound.html; - } -<% } %> + location /notfound.html { + root <%= sourceDir %>/dashboard/dist; + try_files /notfound.html =404; + internal; } + + location / { + root /home/yellowtent/boxdata; + try_files /custom_pages/notfound.html /notfound.html; + } +<% } %> } diff --git a/src/reverseproxy.js b/src/reverseproxy.js index d1eaf3955..6685a7e92 100644 --- a/src/reverseproxy.js +++ b/src/reverseproxy.js @@ -57,6 +57,14 @@ var acme2 = require('./cert/acme2.js'), var NGINX_APPCONFIG_EJS = fs.readFileSync(__dirname + '/nginxconfig.ejs', { encoding: 'utf8' }), RELOAD_NGINX_CMD = path.join(__dirname, 'scripts/reloadnginx.sh'); +function nginxLocation(s) { + if (!s.startsWith('!')) return s; + + let re = s.replace(/[\^$\\.*+?()[\]{}|]/g, '\\$&'); // https://github.com/es-shims/regexp.escape/blob/master/implementation.js + + return `~ ^(?!(${re.slice(1)}))`; // negative regex assertion - https://stackoverflow.com/questions/16302897/nginx-location-not-equal-to-regex +} + function getAcmeApi(domainObject, callback) { assert.strictEqual(typeof domainObject, 'object'); assert.strictEqual(typeof callback, 'function'); @@ -383,7 +391,7 @@ function writeDashboardNginxConfig(bundle, configFileName, vhost, callback) { certFilePath: bundle.certFilePath, keyFilePath: bundle.keyFilePath, robotsTxtQuoted: JSON.stringify('User-agent: *\nDisallow: /\n'), - proxyAuth: { enabled: false, id: null, path: '/' } + proxyAuth: { enabled: false, id: null, location: nginxLocation('/') } }; var nginxConf = ejs.render(NGINX_APPCONFIG_EJS, data); var nginxConfigFilename = path.join(paths.NGINX_APPCONFIG_DIR, configFileName); @@ -463,7 +471,7 @@ function writeAppNginxConfig(app, bundle, callback) { proxyAuth: { enabled: app.sso && app.manifest.addons && app.manifest.addons.proxyAuth, id: app.id, - path: safe.query(app.manifest, 'addons.proxyAuth.path') || '/' + location: nginxLocation(safe.query(app.manifest, 'addons.proxyAuth.path') || '/') }, httpPaths: app.manifest.httpPaths || {} }; @@ -497,7 +505,7 @@ function writeAppRedirectNginxConfig(app, fqdn, bundle, callback) { robotsTxtQuoted: null, cspQuoted: null, hideHeaders: [], - proxyAuth: { enabled: false, id: app.id, path: '/' } + proxyAuth: { enabled: false, id: app.id, location: nginxLocation('/') } }; var nginxConf = ejs.render(NGINX_APPCONFIG_EJS, data); @@ -685,7 +693,7 @@ function writeDefaultConfig(options, callback) { certFilePath, keyFilePath, robotsTxtQuoted: JSON.stringify('User-agent: *\nDisallow: /\n'), - proxyAuth: { enabled: false, id: null, path: '/' } + proxyAuth: { enabled: false, id: null, location: nginxLocation('/') } }; const nginxConf = ejs.render(NGINX_APPCONFIG_EJS, data); const nginxConfigFilename = path.join(paths.NGINX_APPCONFIG_DIR, constants.NGINX_DEFAULT_CONFIG_FILE_NAME);