Skip to content

Commit 2a3d792

Browse files
committed
Fixes #68 - HSTS is now part of the UI
1 parent 4d75427 commit 2a3d792

File tree

24 files changed

+436
-94
lines changed

24 files changed

+436
-94
lines changed

rootfs/etc/nginx/conf.d/include/ssl-ciphers.conf

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,3 @@ ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA
77
ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AE
88
S128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
99
ssl_prefer_server_ciphers on;
10-
11-
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
12-
add_header Strict-Transport-Security max-age=15768000;

src/backend/internal/dead-host.js

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const internalDeadHost = {
4747
.then(() => {
4848
// At this point the domains should have been checked
4949
data.owner_user_id = access.token.getUserId(1);
50+
data = internalHost.cleanSslHstsData(data);
5051

5152
return deadHostModel
5253
.query()
@@ -89,11 +90,11 @@ const internalDeadHost = {
8990

9091
// Add to audit log
9192
return internalAuditLog.add(access, {
92-
action: 'created',
93-
object_type: 'dead-host',
94-
object_id: row.id,
95-
meta: data
96-
})
93+
action: 'created',
94+
object_type: 'dead-host',
95+
object_id: row.id,
96+
meta: data
97+
})
9798
.then(() => {
9899
return row;
99100
});
@@ -144,9 +145,9 @@ const internalDeadHost = {
144145

145146
if (create_certificate) {
146147
return internalCertificate.createQuickCertificate(access, {
147-
domain_names: data.domain_names || row.domain_names,
148-
meta: _.assign({}, row.meta, data.meta)
149-
})
148+
domain_names: data.domain_names || row.domain_names,
149+
meta: _.assign({}, row.meta, data.meta)
150+
})
150151
.then(cert => {
151152
// update host with cert id
152153
data.certificate_id = cert.id;
@@ -162,7 +163,9 @@ const internalDeadHost = {
162163
// Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here.
163164
data = _.assign({}, {
164165
domain_names: row.domain_names
165-
},data);
166+
}, data);
167+
168+
data = internalHost.cleanSslHstsData(data, row);
166169

167170
return deadHostModel
168171
.query()
@@ -171,27 +174,27 @@ const internalDeadHost = {
171174
.then(saved_row => {
172175
// Add to audit log
173176
return internalAuditLog.add(access, {
174-
action: 'updated',
175-
object_type: 'dead-host',
176-
object_id: row.id,
177-
meta: data
178-
})
177+
action: 'updated',
178+
object_type: 'dead-host',
179+
object_id: row.id,
180+
meta: data
181+
})
179182
.then(() => {
180183
return _.omit(saved_row, omissions());
181184
});
182185
});
183186
})
184187
.then(() => {
185188
return internalDeadHost.get(access, {
186-
id: data.id,
187-
expand: ['owner', 'certificate']
188-
})
189+
id: data.id,
190+
expand: ['owner', 'certificate']
191+
})
189192
.then(row => {
190193
// Configure nginx
191194
return internalNginx.configure(deadHostModel, 'dead_host', row)
192195
.then(new_meta => {
193196
row.meta = new_meta;
194-
row = internalHost.cleanRowCertificateMeta(row);
197+
row = internalHost.cleanRowCertificateMeta(row);
195198
return _.omit(row, omissions());
196199
});
197200
});

src/backend/internal/host.js

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,40 @@
1-
'use strict';
2-
1+
const _ = require('lodash');
32
const proxyHostModel = require('../models/proxy_host');
43
const redirectionHostModel = require('../models/redirection_host');
54
const deadHostModel = require('../models/dead_host');
65

76
const internalHost = {
87

8+
/**
9+
* Makes sure that the ssl_* and hsts_* fields play nicely together.
10+
* ie: if there is no cert, then force_ssl is off.
11+
* if force_ssl is off, then hsts_enabled is definitely off.
12+
*
13+
* @param {object} data
14+
* @param {object} [existing_data]
15+
* @returns {object}
16+
*/
17+
cleanSslHstsData: function (data, existing_data) {
18+
existing_data = existing_data === undefined ? {} : existing_data;
19+
20+
let combined_data = _.assign({}, existing_data, data);
21+
22+
if (!combined_data.certificate_id) {
23+
combined_data.ssl_forced = false;
24+
combined_data.http2_support = false;
25+
}
26+
27+
if (!combined_data.ssl_forced) {
28+
combined_data.hsts_enabled = false;
29+
}
30+
31+
if (!combined_data.hsts_enabled) {
32+
combined_data.hsts_subdomains = false;
33+
}
34+
35+
return combined_data;
36+
},
37+
938
/**
1039
* used by the getAll functions of hosts, this removes the certificate meta if present
1140
*

src/backend/internal/proxy-host.js

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
'use strict';
2-
31
const _ = require('lodash');
42
const error = require('../lib/error');
53
const proxyHostModel = require('../models/proxy_host');
@@ -47,6 +45,7 @@ const internalProxyHost = {
4745
.then(() => {
4846
// At this point the domains should have been checked
4947
data.owner_user_id = access.token.getUserId(1);
48+
data = internalHost.cleanSslHstsData(data);
5049

5150
return proxyHostModel
5251
.query()
@@ -90,11 +89,11 @@ const internalProxyHost = {
9089

9190
// Add to audit log
9291
return internalAuditLog.add(access, {
93-
action: 'created',
94-
object_type: 'proxy-host',
95-
object_id: row.id,
96-
meta: data
97-
})
92+
action: 'created',
93+
object_type: 'proxy-host',
94+
object_id: row.id,
95+
meta: data
96+
})
9897
.then(() => {
9998
return row;
10099
});
@@ -109,7 +108,7 @@ const internalProxyHost = {
109108
*/
110109
update: (access, data) => {
111110
let create_certificate = data.certificate_id === 'new';
112-
111+
console.log('PH UPDATE:', data);
113112
if (create_certificate) {
114113
delete data.certificate_id;
115114
}
@@ -145,9 +144,9 @@ const internalProxyHost = {
145144

146145
if (create_certificate) {
147146
return internalCertificate.createQuickCertificate(access, {
148-
domain_names: data.domain_names || row.domain_names,
149-
meta: _.assign({}, row.meta, data.meta)
150-
})
147+
domain_names: data.domain_names || row.domain_names,
148+
meta: _.assign({}, row.meta, data.meta)
149+
})
151150
.then(cert => {
152151
// update host with cert id
153152
data.certificate_id = cert.id;
@@ -165,28 +164,30 @@ const internalProxyHost = {
165164
domain_names: row.domain_names
166165
}, data);
167166

167+
data = internalHost.cleanSslHstsData(data, row);
168+
168169
return proxyHostModel
169170
.query()
170171
.where({id: data.id})
171172
.patch(data)
172173
.then(saved_row => {
173174
// Add to audit log
174175
return internalAuditLog.add(access, {
175-
action: 'updated',
176-
object_type: 'proxy-host',
177-
object_id: row.id,
178-
meta: data
179-
})
176+
action: 'updated',
177+
object_type: 'proxy-host',
178+
object_id: row.id,
179+
meta: data
180+
})
180181
.then(() => {
181182
return _.omit(saved_row, omissions());
182183
});
183184
});
184185
})
185186
.then(() => {
186187
return internalProxyHost.get(access, {
187-
id: data.id,
188-
expand: ['owner', 'certificate', 'access_list']
189-
})
188+
id: data.id,
189+
expand: ['owner', 'certificate', 'access_list']
190+
})
190191
.then(row => {
191192
// Configure nginx
192193
return internalNginx.configure(proxyHostModel, 'proxy_host', row)

src/backend/internal/redirection-host.js

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const internalRedirectionHost = {
4747
.then(() => {
4848
// At this point the domains should have been checked
4949
data.owner_user_id = access.token.getUserId(1);
50+
data = internalHost.cleanSslHstsData(data);
5051

5152
return redirectionHostModel
5253
.query()
@@ -89,11 +90,11 @@ const internalRedirectionHost = {
8990

9091
// Add to audit log
9192
return internalAuditLog.add(access, {
92-
action: 'created',
93-
object_type: 'redirection-host',
94-
object_id: row.id,
95-
meta: data
96-
})
93+
action: 'created',
94+
object_type: 'redirection-host',
95+
object_id: row.id,
96+
meta: data
97+
})
9798
.then(() => {
9899
return row;
99100
});
@@ -144,9 +145,9 @@ const internalRedirectionHost = {
144145

145146
if (create_certificate) {
146147
return internalCertificate.createQuickCertificate(access, {
147-
domain_names: data.domain_names || row.domain_names,
148-
meta: _.assign({}, row.meta, data.meta)
149-
})
148+
domain_names: data.domain_names || row.domain_names,
149+
meta: _.assign({}, row.meta, data.meta)
150+
})
150151
.then(cert => {
151152
// update host with cert id
152153
data.certificate_id = cert.id;
@@ -162,7 +163,9 @@ const internalRedirectionHost = {
162163
// Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here.
163164
data = _.assign({}, {
164165
domain_names: row.domain_names
165-
},data);
166+
}, data);
167+
168+
data = internalHost.cleanSslHstsData(data, row);
166169

167170
return redirectionHostModel
168171
.query()
@@ -171,27 +174,27 @@ const internalRedirectionHost = {
171174
.then(saved_row => {
172175
// Add to audit log
173176
return internalAuditLog.add(access, {
174-
action: 'updated',
175-
object_type: 'redirection-host',
176-
object_id: row.id,
177-
meta: data
178-
})
177+
action: 'updated',
178+
object_type: 'redirection-host',
179+
object_id: row.id,
180+
meta: data
181+
})
179182
.then(() => {
180183
return _.omit(saved_row, omissions());
181184
});
182185
});
183186
})
184187
.then(() => {
185188
return internalRedirectionHost.get(access, {
186-
id: data.id,
187-
expand: ['owner', 'certificate']
188-
})
189+
id: data.id,
190+
expand: ['owner', 'certificate']
191+
})
189192
.then(row => {
190193
// Configure nginx
191194
return internalNginx.configure(redirectionHostModel, 'redirection_host', row)
192195
.then(new_meta => {
193196
row.meta = new_meta;
194-
row = internalHost.cleanRowCertificateMeta(row);
197+
row = internalHost.cleanRowCertificateMeta(row);
195198
return _.omit(row, omissions());
196199
});
197200
});
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict';
2+
3+
const migrate_name = 'hsts';
4+
const logger = require('../logger').migrate;
5+
6+
/**
7+
* Migrate
8+
*
9+
* @see http://knexjs.org/#Schema
10+
*
11+
* @param {Object} knex
12+
* @param {Promise} Promise
13+
* @returns {Promise}
14+
*/
15+
exports.up = function (knex/*, Promise*/) {
16+
logger.info('[' + migrate_name + '] Migrating Up...');
17+
18+
return knex.schema.table('proxy_host', function (proxy_host) {
19+
proxy_host.integer('hsts_enabled').notNull().unsigned().defaultTo(0);
20+
proxy_host.integer('hsts_subdomains').notNull().unsigned().defaultTo(0);
21+
})
22+
.then(() => {
23+
logger.info('[' + migrate_name + '] proxy_host Table altered');
24+
25+
return knex.schema.table('redirection_host', function (redirection_host) {
26+
redirection_host.integer('hsts_enabled').notNull().unsigned().defaultTo(0);
27+
redirection_host.integer('hsts_subdomains').notNull().unsigned().defaultTo(0);
28+
});
29+
})
30+
.then(() => {
31+
logger.info('[' + migrate_name + '] redirection_host Table altered');
32+
33+
return knex.schema.table('dead_host', function (dead_host) {
34+
dead_host.integer('hsts_enabled').notNull().unsigned().defaultTo(0);
35+
dead_host.integer('hsts_subdomains').notNull().unsigned().defaultTo(0);
36+
});
37+
})
38+
.then(() => {
39+
logger.info('[' + migrate_name + '] dead_host Table altered');
40+
});
41+
};
42+
43+
/**
44+
* Undo Migrate
45+
*
46+
* @param {Object} knex
47+
* @param {Promise} Promise
48+
* @returns {Promise}
49+
*/
50+
exports.down = function (knex, Promise) {
51+
logger.warn('[' + migrate_name + '] You can\'t migrate down this one.');
52+
return Promise.resolve(true);
53+
};

src/backend/schema/definitions.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,16 @@
187187
"example": false,
188188
"type": "boolean"
189189
},
190+
"hsts_enabled": {
191+
"description": "Is HSTS Enabled",
192+
"example": false,
193+
"type": "boolean"
194+
},
195+
"hsts_subdomains": {
196+
"description": "Is HSTS applicable to all subdomains",
197+
"example": false,
198+
"type": "boolean"
199+
},
190200
"ssl_provider": {
191201
"type": "string",
192202
"pattern": "^(letsencrypt|other)$"

0 commit comments

Comments
 (0)