aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcel <cel@lOUVT+Phkvai9a/cCS/RKo+S9hnPAQdVixms/7ldpPA=.ed25519>2020-03-28 15:27:10 -0400
committercel <cel@lOUVT+Phkvai9a/cCS/RKo+S9hnPAQdVixms/7ldpPA=.ed25519>2020-04-10 16:11:50 -0400
commit5a719f74ab1963f17275e28ae0aa9779b4b1ec03 (patch)
treecd897362837591a6ef434fa5fe119cd20e663ae0
parentc87ebee6a707d1031fedf2247b440e6aab04332d (diff)
downloadpatchfoo-5a719f74ab1963f17275e28ae0aa9779b4b1ec03.tar.gz
patchfoo-5a719f74ab1963f17275e28ae0aa9779b4b1ec03.zip
Restrict access based on Host header
-rw-r--r--README.md1
-rw-r--r--lib/app.js37
-rw-r--r--lib/serve.js6
3 files changed, 43 insertions, 1 deletions
diff --git a/README.md b/README.md
index 423442c..85afdf7 100644
--- a/README.md
+++ b/README.md
@@ -122,6 +122,7 @@ To make config options persistent, set them in `~/.ssb/config`, e.g.:
- `encode_msgids`: whether to URL-encode message ids in local links. default: `true`
- `auth`: HTTP auth password. default: `null` (no password required)
- `allowAddresses`: Array of IP addresses allowed to connect. default: `null` (allow any to connect). Note if host is `localhost` then this setting is useless.
+- `allowHosts`: Array of hostnames allowed that patchfoo may be connected at, or `*` to allow using any hostname. Default is to allow patchfoo's configured port, at patchfoo's configured host, `localhost`, `127.0.0.1` or `::1`. If hostname includes trailing colon without port, it means use patchfoo's server port. `*` for the port means allow connections at any port. If hostname begins with `.`, subdomains under it are allowed too.
- `filter`: Filter setting. `"all"` to show all messages. `"invert"` to show messages that would be hidden by the default setting. Otherwise the default setting applies, which is so to only show messages authored or upvoted by yourself or by a feed that you you follow. Exceptions are that if you navigate to a user feed page, you will see messages authored by that feed, and if you navigate to a message page, you will see that message - regardless of the filter setting. The `filter` setting may also be specified per-request as a query string parameter.
- `showPrivates`: Whether or not to show private messages. Default is `true`. Overridden by `filter=all`.
- `previewVotes`: Whether to preview creating votes/likes/digs (`true`) or publish them immediately (`false`). default: `false`
diff --git a/lib/app.js b/lib/app.js
index ef5cc6a..d4f0c98 100644
--- a/lib/app.js
+++ b/lib/app.js
@@ -35,6 +35,7 @@ function getKey(msg) {
}
function App(sbot, config) {
+ var self = this
this.sbot = sbot
this.config = config
@@ -55,11 +56,31 @@ function App(sbot, config) {
this.voteBranches = !!config.voteBranches
this.copyableIds = config.copyableIds == null ? true : config.copyableIds
- this.hostname = (/:/.test(this.host) ? '[' + this.host + ']' : this.host) + this.port
+ this.hostname = (/:/.test(this.host) ? '[' + this.host + ']' : this.host) + ':' + this.port
this.dir = path.join(config.path, conf.dir || 'patchfoo')
this.scriptDir = path.join(this.dir, conf.scriptDir || 'script')
this.draftsDir = path.join(this.dir, conf.draftsDir || 'drafts')
+ this.allowHosts = u.toArray(conf.allowHosts ||
+ ['localhost:', '127.0.0.1:', '[::1]:', this.hostname])
+ this.allowHostsParsed = this.allowHosts.indexOf('*') > -1 ? [{
+ subdomains: true,
+ host: '',
+ port: '*'
+ }] : this.allowHosts.map(function (hostname) {
+ var m = /^(\.)?(.*?)(:([0-9]*?|\*))?$/.exec(hostname)
+ if (!m) return void console.trace('Unable to parse hostname pattern "'+hostname+'"')
+ var port = !m[3] ? 80 :
+ !m[4] ? Number(self.port) :
+ '*' === m[4] ? '*' : Number(m[4])
+ if (port !== '*' && isNaN(port)) return void console.trace('Unable to parse port in hostname pattern "'+hostname+'". Default port: "'+self.port+'"')
+ return {
+ subdomains: !!m[1],
+ host: m[2],
+ port: port
+ }
+ }).filter(Boolean)
+
var base = conf.base || '/'
this.opts = {
base: base,
@@ -117,6 +138,20 @@ function App(sbot, config) {
]
}
+App.prototype.isAllowedHostHeader = function (hostname) {
+ var m = /^(\[[^\]]+\]|[^:]+)(?::([0-9]+))?/.exec(hostname)
+ if (!m) return void console.error('Unable to parse Host header "'+hostname+'"')
+ var host = m[1], port = Number(m[2] || 80)
+ for (var i = 0; i < this.allowHostsParsed.length; i++) {
+ var allow = this.allowHostsParsed[i]
+ if ((allow.port === '*' || allow.port === port)
+ && (allow.host === '' || allow.host === host ||
+ (allow.subdomains && host.endsWith('.'+allow.host)))
+ ) return true
+ }
+ return false
+}
+
App.prototype.go = function () {
var self = this
var server = http.createServer(function (req, res) {
diff --git a/lib/serve.js b/lib/serve.js
index c3e2115..fdf24f7 100644
--- a/lib/serve.js
+++ b/lib/serve.js
@@ -112,6 +112,12 @@ Serve.prototype.go = function () {
}
}
+ if (!this.app.isAllowedHostHeader(this.req.headers.host)) {
+ console.error('Host header not allowed: "' + this.req.headers.host + '"')
+ this.res.writeHead(403)
+ return this.res.end('Forbidden')
+ }
+
this.replyMentionFeeds = conf.replyMentionFeeds == null ? true :
Boolean(conf.replyMentionFeeds)