From 4159f62e0d0cc0ce3614e81d3b7d64ee4d101be4 Mon Sep 17 00:00:00 2001 From: droguljic Date: Wed, 16 Jun 2021 13:28:27 +0200 Subject: [PATCH 1/3] Allow storage proxy to work with IPv4 Storage proxy signed cookies would get assigned to wrong domain if configured host name is IPv4 address, thus blocking display of assets. Problem is in `psl` package which parses IPv4 addresses, thus producing wrong domains. Fix detects IPv4 addresses using regular expression and just returns `null` for domain. --- server/shared/storage/proxy/mw.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server/shared/storage/proxy/mw.js b/server/shared/storage/proxy/mw.js index 5b43bad14..30635f57c 100644 --- a/server/shared/storage/proxy/mw.js +++ b/server/shared/storage/proxy/mw.js @@ -7,6 +7,13 @@ const path = require('path'); const router = require('express').Router(); const psl = require('psl'); +const IPV4_REGEX = /^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$/; + +function getDomain() { + if (IPV4_REGEX.test(config.hostname)) return null; + return psl.parse(config.hostname).domain; +} + module.exports = (storage, proxy) => { function getFile(req, res, next) { const key = req.params[0]; @@ -24,7 +31,7 @@ module.exports = (storage, proxy) => { if (proxy.hasCookies(req.cookies, repositoryId)) return next(); const maxAge = 1000 * 60 * 60; // 1 hour in ms const cookies = proxy.getSignedCookies(repositoryId, maxAge); - const { domain } = psl.parse(config.hostname); + const domain = getDomain(); const cookieOptions = { domain, maxAge, httpOnly: true }; Object.entries(cookies).forEach(([cookie, value]) => { res.cookie(cookie, value, cookieOptions); From eb19aea7b7dc2d6861d12f413b97cb50fe7d85a6 Mon Sep 17 00:00:00 2001 From: droguljic Date: Fri, 25 Jun 2021 15:42:16 +0200 Subject: [PATCH 2/3] Throw error if CloudFront is used alongside IPv4 Storage proxy middleware will throw an error for IPv4 host name and CloudFront proxy combination. --- server/shared/storage/proxy/mw.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/shared/storage/proxy/mw.js b/server/shared/storage/proxy/mw.js index 30635f57c..389a30199 100644 --- a/server/shared/storage/proxy/mw.js +++ b/server/shared/storage/proxy/mw.js @@ -15,6 +15,10 @@ function getDomain() { } module.exports = (storage, proxy) => { + if (IPV4_REGEX.test(config.hostname) && config.storage.proxy.provider === 'cloudfront') { + throw new Error('CloudFront storage proxy cannot be used alongside IPv4 host name'); + } + function getFile(req, res, next) { const key = req.params[0]; const hasValidCookies = proxy.verifyCookies(req.cookies, key); From 231535b937bd2bb6f3ef2f7fed104766afbc1388 Mon Sep 17 00:00:00 2001 From: droguljic Date: Tue, 6 Jul 2021 12:48:43 +0200 Subject: [PATCH 3/3] Use `is-ip` package for IPv4 address check --- config/server/index.js | 9 +++++++++ package-lock.json | 13 +++++++++++++ package.json | 1 + server/shared/storage/proxy/mw.js | 9 ++------- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/config/server/index.js b/config/server/index.js index bf12b548c..8fa618326 100644 --- a/config/server/index.js +++ b/config/server/index.js @@ -2,6 +2,7 @@ const auth = require('./auth'); const consumer = require('./consumer'); +const isIp = require('is-ip'); const isLocalhost = require('is-localhost'); const mail = require('./mail'); const parse = require('url-parse'); @@ -15,6 +16,8 @@ const port = resolvePort(); const origin = resolveOrigin(hostname, protocol, port); const previewUrl = process.env.PREVIEW_URL; +validateStorageProxy(storage.proxy, hostname); + module.exports = { protocol, hostname, @@ -58,3 +61,9 @@ function resolveOriginPort(hostname) { if (REVERSE_PROXY_PORT === '80' || REVERSE_PROXY_PORT === '443') return ''; return `:${REVERSE_PROXY_PORT}`; } + +function validateStorageProxy(proxy, hostname) { + if (isIp.v4(hostname) && /cloudfront/i.test(proxy.provider)) { + throw new Error('CloudFront storage proxy cannot be used alongside IPv4 host name'); + } +} diff --git a/package-lock.json b/package-lock.json index b5fc58adf..679f245bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11229,6 +11229,11 @@ "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", "dev": true }, + "ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -11471,6 +11476,14 @@ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==" }, + "is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "requires": { + "ip-regex": "^4.0.0" + } + }, "is-localhost": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/is-localhost/-/is-localhost-0.0.2.tgz", diff --git a/package.json b/package.json index fd30c2226..6c71cc13c 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "humanize-string": "^2.1.0", "ioredis": "^4.24.2", "is-iexplorer": "^1.0.0", + "is-ip": "^3.1.0", "is-localhost": "0.0.2", "is-safari": "^1.0.0", "is-url": "^1.2.2", diff --git a/server/shared/storage/proxy/mw.js b/server/shared/storage/proxy/mw.js index 389a30199..59d5ea209 100644 --- a/server/shared/storage/proxy/mw.js +++ b/server/shared/storage/proxy/mw.js @@ -2,23 +2,18 @@ const config = require('../../../../config/server'); const { FORBIDDEN } = require('http-status-codes'); +const isIp = require('is-ip'); const miss = require('mississippi'); const path = require('path'); const router = require('express').Router(); const psl = require('psl'); -const IPV4_REGEX = /^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$/; - function getDomain() { - if (IPV4_REGEX.test(config.hostname)) return null; + if (isIp.v4(config.hostname)) return null; return psl.parse(config.hostname).domain; } module.exports = (storage, proxy) => { - if (IPV4_REGEX.test(config.hostname) && config.storage.proxy.provider === 'cloudfront') { - throw new Error('CloudFront storage proxy cannot be used alongside IPv4 host name'); - } - function getFile(req, res, next) { const key = req.params[0]; const hasValidCookies = proxy.verifyCookies(req.cookies, key);