Finished importer, advanced nginx config for hosts, custom certs used in nginx templates

This commit is contained in:
Jamie Curnow 2018-08-22 14:31:03 +10:00
parent d092d4bbe7
commit c543a1dc34
20 changed files with 174 additions and 72 deletions

View File

@ -2,14 +2,12 @@
In order of importance, somewhat.. In order of importance, somewhat..
- v1 Importer - Nginx config output:
- ssl certificates - Redirection host preserve path nginx configuration
- nginx advanced config
- Redirection host preserve path nginx configuration
- Custom ssl certificate saving to disk and usage in nginx configs
- Dashboard stats are caching instead of querying - Dashboard stats are caching instead of querying
- UI Log tail - UI Log tail
- Custom Nginx Config Editor - Custom Nginx Config Editor
- Enable/Disable a config
Testing: Testing:

View File

@ -3,6 +3,7 @@
mkdir -p /tmp/nginx/body \ mkdir -p /tmp/nginx/body \
/var/log/nginx \ /var/log/nginx \
/data/nginx \ /data/nginx \
/data/custom_ssl \
/data/logs \ /data/logs \
/data/access \ /data/access \
/data/nginx/proxy_host \ /data/nginx/proxy_host \

View File

@ -186,8 +186,6 @@ module.exports = function () {
// 2. rename archive folder name // 2. rename archive folder name
new_archive_path = new_archive_path + 'npm-' + certificate.id; new_archive_path = new_archive_path + 'npm-' + certificate.id;
//logger.debug('Renaming archive folder:', full_archive_path, '->', new_archive_path);
fs.renameSync(full_archive_path, new_archive_path); fs.renameSync(full_archive_path, new_archive_path);
return certificate; return certificate;
@ -195,9 +193,6 @@ module.exports = function () {
.then(certificate => { .then(certificate => {
// 3. rename live folder name // 3. rename live folder name
new_live_path = new_live_path + 'npm-' + certificate.id; new_live_path = new_live_path + 'npm-' + certificate.id;
//logger.debug('Renaming live folder:', full_live_path, '->', new_live_path);
fs.renameSync(full_live_path, new_live_path); fs.renameSync(full_live_path, new_live_path);
// and also update the symlinks in this folder: // and also update the symlinks in this folder:
@ -211,8 +206,6 @@ module.exports = function () {
]; ];
names.map(function (name) { names.map(function (name) {
//logger.debug('Live Link:', name);
// remove symlink // remove symlink
try { try {
fs.unlinkSync(new_live_path + '/' + name[0]); fs.unlinkSync(new_live_path + '/' + name[0]);
@ -221,7 +214,6 @@ module.exports = function () {
logger.error(err); logger.error(err);
} }
//logger.debug('Creating Link:', '../../archive/npm-' + certificate.id + '/' + name[1]);
// create new symlink // create new symlink
fs.symlinkSync('../../archive/npm-' + certificate.id + '/' + name[1], name[0]); fs.symlinkSync('../../archive/npm-' + certificate.id + '/' + name[1], name[0]);
}); });
@ -356,8 +348,6 @@ module.exports = function () {
certificate_id = certificate_map[host.hostname]; certificate_id = certificate_map[host.hostname];
} }
// TODO: Advanced nginx config
return proxyHostModel return proxyHostModel
.query() .query()
.insertAndFetch({ .insertAndFetch({
@ -370,6 +360,7 @@ module.exports = function () {
ssl_forced: host.force_ssl || false, ssl_forced: host.force_ssl || false,
caching_enabled: host.asset_caching || false, caching_enabled: host.asset_caching || false,
block_exploits: host.block_exploits || false, block_exploits: host.block_exploits || false,
advanced_config: host.advanced || '',
meta: meta meta: meta
}) })
.then(row => { .then(row => {
@ -405,16 +396,15 @@ module.exports = function () {
certificate_id = certificate_map[host.hostname]; certificate_id = certificate_map[host.hostname];
} }
// TODO: Advanced nginx config
return deadHostModel return deadHostModel
.query() .query()
.insertAndFetch({ .insertAndFetch({
owner_user_id: 1, owner_user_id: 1,
domain_names: [host.hostname], domain_names: [host.hostname],
certificate_id: certificate_id, certificate_id: certificate_id,
ssl_forced: host.force_ssl || false, ssl_forced: host.force_ssl || false,
meta: meta advanced_config: host.advanced || '',
meta: meta
}) })
.then(row => { .then(row => {
// re-fetch with cert // re-fetch with cert
@ -449,8 +439,6 @@ module.exports = function () {
certificate_id = certificate_map[host.hostname]; certificate_id = certificate_map[host.hostname];
} }
// TODO: Advanced nginx config
return redirectionHostModel return redirectionHostModel
.query() .query()
.insertAndFetch({ .insertAndFetch({
@ -460,6 +448,7 @@ module.exports = function () {
block_exploits: host.block_exploits || false, block_exploits: host.block_exploits || false,
certificate_id: certificate_id, certificate_id: certificate_id,
ssl_forced: host.force_ssl || false, ssl_forced: host.force_ssl || false,
advanced_config: host.advanced || '',
meta: meta meta: meta
}) })
.then(row => { .then(row => {
@ -483,8 +472,6 @@ module.exports = function () {
const importStream = function (access, host) { const importStream = function (access, host) {
logger.info('Creating Stream: ' + host.incoming_port); logger.info('Creating Stream: ' + host.incoming_port);
// TODO: Advanced nginx config
return streamModel return streamModel
.query() .query()
.insertAndFetch({ .insertAndFetch({
@ -537,7 +524,7 @@ module.exports = function () {
}) })
.then(() => { .then(() => {
// Write the /config/v2-imported file so we don't import again // Write the /config/v2-imported file so we don't import again
fs.writeFile('/config/v2-imported', 'true', function(err) { fs.writeFile('/config/v2-imported', 'true', function (err) {
if (err) { if (err) {
logger.err(err); logger.err(err);
} }

View File

@ -183,7 +183,10 @@ const internalCertificate = {
}); });
}); });
} else { } else {
return certificate; return internalCertificate.writeCustomCert(certificate)
.then(() => {
return certificate;
});
} }
}).then(certificate => { }).then(certificate => {
@ -401,6 +404,54 @@ const internalCertificate = {
}); });
}, },
/**
* @param {Object} certificate
* @returns {Promise}
*/
writeCustomCert: certificate => {
return new Promise((resolve, reject) => {
let dir = '/data/custom_ssl/npm-' + certificate.id;
if (certificate.provider === 'letsencrypt') {
reject(new Error('Refusing to write letsencrypt certs here'));
return;
}
let cert_data = certificate.meta.certificate;
if (typeof certificate.meta.intermediate_certificate !== 'undefined') {
cert_data = cert_data + "\n" + certificate.meta.intermediate_certificate;
}
try {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
} catch (err) {
reject(err);
return;
}
fs.writeFile(dir + '/fullchain.pem', cert_data, function (err) {
if (err) {
reject(err);
} else {
resolve();
}
});
})
.then(() => {
return new Promise((resolve, reject) => {
fs.writeFile(dir + '/privkey.pem', certificate.meta.certificate_key, function (err) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
});
},
/** /**
* @param {Access} access * @param {Access} access
* @param {Object} data * @param {Object} data

View File

@ -76,6 +76,7 @@ exports.up = function (knex/*, Promise*/) {
table.integer('ssl_forced').notNull().unsigned().defaultTo(0); table.integer('ssl_forced').notNull().unsigned().defaultTo(0);
table.integer('caching_enabled').notNull().unsigned().defaultTo(0); table.integer('caching_enabled').notNull().unsigned().defaultTo(0);
table.integer('block_exploits').notNull().unsigned().defaultTo(0); table.integer('block_exploits').notNull().unsigned().defaultTo(0);
table.text('advanced_config').notNull().defaultTo('');
table.json('meta').notNull().defaultTo('{}'); table.json('meta').notNull().defaultTo('{}');
}); });
}) })
@ -94,6 +95,7 @@ exports.up = function (knex/*, Promise*/) {
table.integer('certificate_id').notNull().unsigned().defaultTo(0); table.integer('certificate_id').notNull().unsigned().defaultTo(0);
table.integer('ssl_forced').notNull().unsigned().defaultTo(0); table.integer('ssl_forced').notNull().unsigned().defaultTo(0);
table.integer('block_exploits').notNull().unsigned().defaultTo(0); table.integer('block_exploits').notNull().unsigned().defaultTo(0);
table.text('advanced_config').notNull().defaultTo('');
table.json('meta').notNull().defaultTo('{}'); table.json('meta').notNull().defaultTo('{}');
}); });
}) })
@ -109,6 +111,7 @@ exports.up = function (knex/*, Promise*/) {
table.json('domain_names').notNull(); table.json('domain_names').notNull();
table.integer('certificate_id').notNull().unsigned().defaultTo(0); table.integer('certificate_id').notNull().unsigned().defaultTo(0);
table.integer('ssl_forced').notNull().unsigned().defaultTo(0); table.integer('ssl_forced').notNull().unsigned().defaultTo(0);
table.text('advanced_config').notNull().defaultTo('');
table.json('meta').notNull().defaultTo('{}'); table.json('meta').notNull().defaultTo('{}');
}); });
}) })

View File

@ -24,18 +24,11 @@
"ssl_forced": { "ssl_forced": {
"$ref": "../definitions.json#/definitions/ssl_forced" "$ref": "../definitions.json#/definitions/ssl_forced"
}, },
"advanced_config": {
"type": "string"
},
"meta": { "meta": {
"type": "object", "type": "object"
"additionalProperties": false,
"properties": {
"letsencrypt_email": {
"type": "string",
"format": "email"
},
"letsencrypt_agree": {
"type": "boolean"
}
}
} }
}, },
"properties": { "properties": {
@ -57,6 +50,9 @@
"ssl_forced": { "ssl_forced": {
"$ref": "#/definitions/ssl_forced" "$ref": "#/definitions/ssl_forced"
}, },
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"meta": { "meta": {
"$ref": "#/definitions/meta" "$ref": "#/definitions/meta"
} }
@ -105,6 +101,9 @@
"ssl_forced": { "ssl_forced": {
"$ref": "#/definitions/ssl_forced" "$ref": "#/definitions/ssl_forced"
}, },
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"meta": { "meta": {
"$ref": "#/definitions/meta" "$ref": "#/definitions/meta"
} }
@ -139,6 +138,9 @@
"ssl_forced": { "ssl_forced": {
"$ref": "#/definitions/ssl_forced" "$ref": "#/definitions/ssl_forced"
}, },
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"meta": { "meta": {
"$ref": "#/definitions/meta" "$ref": "#/definitions/meta"
} }

View File

@ -42,6 +42,9 @@
"access_list_id": { "access_list_id": {
"$ref": "../definitions.json#/definitions/access_list_id" "$ref": "../definitions.json#/definitions/access_list_id"
}, },
"advanced_config": {
"type": "string"
},
"meta": { "meta": {
"type": "object" "type": "object"
} }
@ -80,6 +83,9 @@
"access_list_id": { "access_list_id": {
"$ref": "#/definitions/access_list_id" "$ref": "#/definitions/access_list_id"
}, },
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"meta": { "meta": {
"$ref": "#/definitions/meta" "$ref": "#/definitions/meta"
} }
@ -145,6 +151,9 @@
"access_list_id": { "access_list_id": {
"$ref": "#/definitions/access_list_id" "$ref": "#/definitions/access_list_id"
}, },
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"meta": { "meta": {
"$ref": "#/definitions/meta" "$ref": "#/definitions/meta"
} }
@ -194,6 +203,9 @@
"access_list_id": { "access_list_id": {
"$ref": "#/definitions/access_list_id" "$ref": "#/definitions/access_list_id"
}, },
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"meta": { "meta": {
"$ref": "#/definitions/meta" "$ref": "#/definitions/meta"
} }

View File

@ -35,18 +35,11 @@
"block_exploits": { "block_exploits": {
"$ref": "../definitions.json#/definitions/block_exploits" "$ref": "../definitions.json#/definitions/block_exploits"
}, },
"advanced_config": {
"type": "string"
},
"meta": { "meta": {
"type": "object", "type": "object"
"additionalProperties": false,
"properties": {
"letsencrypt_email": {
"type": "string",
"format": "email"
},
"letsencrypt_agree": {
"type": "boolean"
}
}
} }
}, },
"properties": { "properties": {
@ -77,6 +70,9 @@
"block_exploits": { "block_exploits": {
"$ref": "#/definitions/block_exploits" "$ref": "#/definitions/block_exploits"
}, },
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"meta": { "meta": {
"$ref": "#/definitions/meta" "$ref": "#/definitions/meta"
} }
@ -135,6 +131,9 @@
"block_exploits": { "block_exploits": {
"$ref": "#/definitions/block_exploits" "$ref": "#/definitions/block_exploits"
}, },
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"meta": { "meta": {
"$ref": "#/definitions/meta" "$ref": "#/definitions/meta"
} }
@ -178,6 +177,9 @@
"block_exploits": { "block_exploits": {
"$ref": "#/definitions/block_exploits" "$ref": "#/definitions/block_exploits"
}, },
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"meta": { "meta": {
"$ref": "#/definitions/meta" "$ref": "#/definitions/meta"
} }

View File

@ -6,5 +6,6 @@
ssl_certificate /etc/letsencrypt/live/npm-{{ certificate_id }}/fullchain.pem; ssl_certificate /etc/letsencrypt/live/npm-{{ certificate_id }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/npm-{{ certificate_id }}/privkey.pem; ssl_certificate_key /etc/letsencrypt/live/npm-{{ certificate_id }}/privkey.pem;
{% endif %} {% endif %}
# TODO: Custom SSL paths ssl_certificate /data/custom_ssl/npm-{{ certificate_id }}/fullchain.pem;
ssl_certificate_key /data/custom_ssl/npm-{{ certificate_id }}/privkey.pem;
{% endif %} {% endif %}

View File

@ -6,7 +6,7 @@ server {
access_log /data/logs/dead_host-{{ id }}.log proxy; access_log /data/logs/dead_host-{{ id }}.log proxy;
# TODO: Advanced config options {{ advanced_config }}
return 404; return 404;
} }

View File

@ -11,7 +11,7 @@ server {
access_log /data/logs/proxy_host-{{ id }}.log proxy; access_log /data/logs/proxy_host-{{ id }}.log proxy;
# TODO: Advanced config options {{ advanced_config }}
location / { location / {
{%- if access_list_id > 0 -%} {%- if access_list_id > 0 -%}

View File

@ -8,7 +8,7 @@ server {
access_log /data/logs/redirection_host-{{ id }}.log proxy; access_log /data/logs/redirection_host-{{ id }}.log proxy;
# TODO: Advanced config options {{ advanced_config }}
# TODO: Preserve Path Option # TODO: Preserve Path Option

View File

@ -8,6 +8,7 @@
<ul class="nav nav-tabs" role="tablist"> <ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> <%- i18n('all-hosts', 'details') %></a></li> <li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> <%- i18n('all-hosts', 'details') %></a></li>
<li role="presentation" class="nav-item"><a href="#ssl-options" aria-controls="tab2" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-shield"></i> <%- i18n('str', 'ssl') %></a></li> <li role="presentation" class="nav-item"><a href="#ssl-options" aria-controls="tab2" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-shield"></i> <%- i18n('str', 'ssl') %></a></li>
<li role="presentation" class="nav-item"><a href="#advanced" aria-controls="tab3" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-settings"></i> <%- i18n('all-hosts', 'advanced') %></a></li>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<!-- Details --> <!-- Details -->
@ -63,9 +64,19 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Advanced -->
<div role="tabpanel" class="tab-pane" id="advanced">
<div class="row">
<div class="col-md-12">
<div class="form-group mb-0">
<label class="form-label"><%- i18n('all-hosts', 'advanced-config') %></label>
<textarea name="advanced_config" rows="8" class="form-control text-monospace" placeholder="# <%- i18n('all-hosts', 'advanced-warning') %>"><%- advanced_config %></textarea>
</div>
</div>
</div>
</div>
</div> </div>
</form> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">

View File

@ -8,12 +8,12 @@
<ul class="nav nav-tabs" role="tablist"> <ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> <%- i18n('all-hosts', 'details') %></a></li> <li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> <%- i18n('all-hosts', 'details') %></a></li>
<li role="presentation" class="nav-item"><a href="#ssl-options" aria-controls="tab2" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-shield"></i> <%- i18n('str', 'ssl') %></a></li> <li role="presentation" class="nav-item"><a href="#ssl-options" aria-controls="tab2" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-shield"></i> <%- i18n('str', 'ssl') %></a></li>
<li role="presentation" class="nav-item"><a href="#advanced" aria-controls="tab3" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-settings"></i> <%- i18n('all-hosts', 'advanced') %></a></li>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<!-- Details --> <!-- Details -->
<div role="tabpanel" class="tab-pane active" id="details"> <div role="tabpanel" class="tab-pane active" id="details">
<div class="row"> <div class="row">
<div class="col-sm-12 col-md-12"> <div class="col-sm-12 col-md-12">
<div class="form-group"> <div class="form-group">
<label class="form-label"><%- i18n('all-hosts', 'domain-names') %> <span class="form-required">*</span></label> <label class="form-label"><%- i18n('all-hosts', 'domain-names') %> <span class="form-required">*</span></label>
@ -101,9 +101,19 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Advanced -->
<div role="tabpanel" class="tab-pane" id="advanced">
<div class="row">
<div class="col-md-12">
<div class="form-group mb-0">
<label class="form-label"><%- i18n('all-hosts', 'advanced-config') %></label>
<textarea name="advanced_config" rows="8" class="form-control text-monospace" placeholder="# <%- i18n('all-hosts', 'advanced-warning') %>"><%- advanced_config %></textarea>
</div>
</div>
</div>
</div>
</div> </div>
</form> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">

View File

@ -8,6 +8,7 @@
<ul class="nav nav-tabs" role="tablist"> <ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> <%- i18n('all-hosts', 'details') %></a></li> <li role="presentation" class="nav-item"><a href="#details" aria-controls="tab1" role="tab" data-toggle="tab" class="nav-link active"><i class="fe fe-zap"></i> <%- i18n('all-hosts', 'details') %></a></li>
<li role="presentation" class="nav-item"><a href="#ssl-options" aria-controls="tab2" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-shield"></i> <%- i18n('str', 'ssl') %></a></li> <li role="presentation" class="nav-item"><a href="#ssl-options" aria-controls="tab2" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-shield"></i> <%- i18n('str', 'ssl') %></a></li>
<li role="presentation" class="nav-item"><a href="#advanced" aria-controls="tab3" role="tab" data-toggle="tab" class="nav-link"><i class="fe fe-settings"></i> <%- i18n('all-hosts', 'advanced') %></a></li>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<!-- Details --> <!-- Details -->
@ -87,9 +88,19 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Advanced -->
<div role="tabpanel" class="tab-pane" id="advanced">
<div class="row">
<div class="col-md-12">
<div class="form-group mb-0">
<label class="form-label"><%- i18n('all-hosts', 'advanced-config') %></label>
<textarea name="advanced_config" rows="8" class="form-control text-monospace" placeholder="# <%- i18n('all-hosts', 'advanced-warning') %>"><%- advanced_config %></textarea>
</div>
</div>
</div>
</div>
</div> </div>
</form> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">

View File

@ -73,7 +73,10 @@
"none": "None", "none": "None",
"new-cert": "Request a new SSL Certificate", "new-cert": "Request a new SSL Certificate",
"with-le": "with Let's Encrypt", "with-le": "with Let's Encrypt",
"no-ssl": "This host will not use HTTPS" "no-ssl": "This host will not use HTTPS",
"advanced": "Advanced",
"advanced-warning": "Enter your custom Nginx configuration here at your own risk!",
"advanced-config": "Custom Nginx Configuration"
}, },
"ssl": { "ssl": {
"letsencrypt": "Let's Encrypt", "letsencrypt": "Let's Encrypt",

View File

@ -7,16 +7,17 @@ const model = Backbone.Model.extend({
defaults: function () { defaults: function () {
return { return {
id: undefined, id: undefined,
created_on: null, created_on: null,
modified_on: null, modified_on: null,
domain_names: [], domain_names: [],
certificate_id: 0, certificate_id: 0,
ssl_forced: false, ssl_forced: false,
meta: {}, meta: {},
advanced_config: '',
// The following are expansions: // The following are expansions:
owner: null, owner: null,
certificate: null certificate: null
}; };
} }
}); });

View File

@ -18,6 +18,7 @@ const model = Backbone.Model.extend({
ssl_forced: false, ssl_forced: false,
caching_enabled: false, caching_enabled: false,
block_exploits: false, block_exploits: false,
advanced_config: '',
meta: {}, meta: {},
// The following are expansions: // The following are expansions:
owner: null, owner: null,

View File

@ -16,6 +16,7 @@ const model = Backbone.Model.extend({
certificate_id: 0, certificate_id: 0,
ssl_forced: false, ssl_forced: false,
block_exploits: false, block_exploits: false,
advanced_config: '',
meta: {}, meta: {},
// The following are expansions: // The following are expansions:
owner: null, owner: null,

View File

@ -116,3 +116,10 @@ $pink: #f66d9b;
margin: 1.75rem auto; margin: 1.75rem auto;
} }
} }
/* Form mod */
textarea.form-control.text-monospace {
font-size: 12px;
}