mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-08-07 01:43:33 +00:00
first open-appsec support
This commit is contained in:
@@ -4,8 +4,12 @@ const utils = require('../lib/utils');
|
||||
const proxyHostModel = require('../models/proxy_host');
|
||||
const internalHost = require('./host');
|
||||
const internalNginx = require('./nginx');
|
||||
const internalNginxOpenappsec= require('./nginx-openappsec');
|
||||
const internalAuditLog = require('./audit-log');
|
||||
const internalCertificate = require('./certificate');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const yaml = require('js-yaml');
|
||||
|
||||
function omissions () {
|
||||
return ['is_deleted'];
|
||||
@@ -48,9 +52,15 @@ const internalProxyHost = {
|
||||
data.owner_user_id = access.token.getUserId(1);
|
||||
data = internalHost.cleanSslHstsData(data);
|
||||
|
||||
let db_data = _.assign({}, data);
|
||||
// Remove the openappsec fields from data. they are not in the database.
|
||||
delete db_data.use_openappsec;
|
||||
delete db_data.openappsec_mode;
|
||||
delete db_data.minimum_confidence;
|
||||
|
||||
return proxyHostModel
|
||||
.query()
|
||||
.insertAndFetch(data)
|
||||
.insertAndFetch(db_data)
|
||||
.then(utils.omitRow(omissions()));
|
||||
})
|
||||
.then((row) => {
|
||||
@@ -84,6 +94,10 @@ const internalProxyHost = {
|
||||
return row;
|
||||
});
|
||||
})
|
||||
.then(row => {
|
||||
internalNginxOpenappsec.generateConfig(access, row, data)
|
||||
return row;
|
||||
})
|
||||
.then((row) => {
|
||||
// Audit log
|
||||
data.meta = _.assign({}, data.meta || {}, row.meta);
|
||||
@@ -159,6 +173,11 @@ const internalProxyHost = {
|
||||
return row;
|
||||
}
|
||||
})
|
||||
.then(row => {
|
||||
internalNginxOpenappsec.generateConfig(access, row, data);
|
||||
// internalNginxOpenappsec.updateConfig(row, data)
|
||||
return row;
|
||||
})
|
||||
.then((row) => {
|
||||
// Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here.
|
||||
data = _.assign({}, {
|
||||
@@ -167,6 +186,11 @@ const internalProxyHost = {
|
||||
|
||||
data = internalHost.cleanSslHstsData(data, row);
|
||||
|
||||
// Remove the openappsec fields from data. they are not in the database
|
||||
delete data.use_openappsec;
|
||||
delete data.openappsec_mode;
|
||||
delete data.minimum_confidence;
|
||||
|
||||
return proxyHostModel
|
||||
.query()
|
||||
.where({id: data.id})
|
||||
@@ -247,6 +271,22 @@ const internalProxyHost = {
|
||||
if (typeof data.omit !== 'undefined' && data.omit !== null) {
|
||||
row = _.omit(row, data.omit);
|
||||
}
|
||||
return row;
|
||||
})
|
||||
.then((row) => {
|
||||
// add openappsec fields to row
|
||||
try {
|
||||
const configFilePath = internalNginxOpenappsec.getConfigFilePath(access);
|
||||
const openappsecConfig = yaml.load(fs.readFileSync(configFilePath, 'utf8'));
|
||||
let result = internalNginxOpenappsec.getOpenappsecFields(openappsecConfig, row.id);
|
||||
row.use_openappsec = result.use_openappsec;
|
||||
row.openappsec_mode = result.mode;
|
||||
row.minimum_confidence = result.minimum_confidence;
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Error reading openappsec config file: " + e);
|
||||
}
|
||||
|
||||
return row;
|
||||
});
|
||||
},
|
||||
@@ -274,6 +314,10 @@ const internalProxyHost = {
|
||||
.patch({
|
||||
is_deleted: 1
|
||||
})
|
||||
.then(() => {
|
||||
// Delete openappsec config
|
||||
internalNginxOpenappsec.deleteConfig(access, row);
|
||||
})
|
||||
.then(() => {
|
||||
// Delete Nginx Config
|
||||
return internalNginx.deleteConfig('proxy_host', row)
|
||||
@@ -430,6 +474,21 @@ const internalProxyHost = {
|
||||
return query.then(utils.omitRows(omissions()));
|
||||
})
|
||||
.then((rows) => {
|
||||
// add openappsec fields to rows
|
||||
try {
|
||||
const configFilePath = internalNginxOpenappsec.getConfigFilePath(access);
|
||||
const openappsecConfig = yaml.load(fs.readFileSync(configFilePath, 'utf8'));
|
||||
rows.map(function (row, idx) {
|
||||
let result = internalNginxOpenappsec.getOpenappsecFields(openappsecConfig, row.id);
|
||||
rows[idx].use_openappsec = result.use_openappsec;
|
||||
rows[idx].openappsec_mode = result.mode;
|
||||
rows[idx].minimum_confidence = result.minimum_confidence;
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Error reading openappsec config file: " + e);
|
||||
}
|
||||
|
||||
if (typeof expand !== 'undefined' && expand !== null && expand.indexOf('certificate') !== -1) {
|
||||
return internalHost.cleanAllRowsCertificateMeta(rows);
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@
|
||||
"express": "^4.17.3",
|
||||
"express-fileupload": "^1.1.9",
|
||||
"gravatar": "^1.8.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"json-schema-ref-parser": "^8.0.0",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"knex": "2.4.2",
|
||||
|
@@ -29,8 +29,10 @@ router.use('/schema', require('./schema'));
|
||||
router.use('/tokens', require('./tokens'));
|
||||
router.use('/users', require('./users'));
|
||||
router.use('/audit-log', require('./audit-log'));
|
||||
router.use('/openappsec-log', require('./openappsec-log'));
|
||||
router.use('/reports', require('./reports'));
|
||||
router.use('/settings', require('./settings'));
|
||||
router.use('/openappsec-settings', require('./openappsec-settings'));
|
||||
router.use('/nginx/proxy-hosts', require('./nginx/proxy_hosts'));
|
||||
router.use('/nginx/redirection-hosts', require('./nginx/redirection_hosts'));
|
||||
router.use('/nginx/dead-hosts', require('./nginx/dead_hosts'));
|
||||
|
@@ -137,6 +137,18 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"openappsec_mode": {
|
||||
"description": "openappsec_mode ID",
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 255
|
||||
},
|
||||
"minimum_confidence": {
|
||||
"description": "minimum_confidence ID",
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 255
|
||||
},
|
||||
"access_list_id": {
|
||||
"description": "Access List ID",
|
||||
"example": 1234,
|
||||
@@ -231,6 +243,11 @@
|
||||
"example": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"use_openappsec": {
|
||||
"description": "Use openappsec",
|
||||
"example": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"caching_enabled": {
|
||||
"description": "Should we cache assets",
|
||||
"example": true,
|
||||
|
@@ -50,6 +50,15 @@
|
||||
"block_exploits": {
|
||||
"$ref": "../definitions.json#/definitions/block_exploits"
|
||||
},
|
||||
"use_openappsec": {
|
||||
"$ref": "../definitions.json#/definitions/use_openappsec"
|
||||
},
|
||||
"openappsec_mode": {
|
||||
"$ref": "../definitions.json#/definitions/openappsec_mode"
|
||||
},
|
||||
"minimum_confidence": {
|
||||
"$ref": "../definitions.json#/definitions/minimum_confidence"
|
||||
},
|
||||
"caching_enabled": {
|
||||
"$ref": "../definitions.json#/definitions/caching_enabled"
|
||||
},
|
||||
@@ -104,6 +113,15 @@
|
||||
},
|
||||
"advanced_config": {
|
||||
"type": "string"
|
||||
},
|
||||
"use_openappsec": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"openappsec_mode": {
|
||||
"type": "string"
|
||||
},
|
||||
"minimum_confidence": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,6 +167,15 @@
|
||||
"block_exploits": {
|
||||
"$ref": "#/definitions/block_exploits"
|
||||
},
|
||||
"use_openappsec": {
|
||||
"$ref": "#/definitions/use_openappsec"
|
||||
},
|
||||
"openappsec_mode": {
|
||||
"$ref": "#/definitions/openappsec_mode"
|
||||
},
|
||||
"minimum_confidence": {
|
||||
"$ref": "#/definitions/minimum_confidence"
|
||||
},
|
||||
"caching_enabled": {
|
||||
"$ref": "#/definitions/caching_enabled"
|
||||
},
|
||||
@@ -239,6 +266,15 @@
|
||||
"block_exploits": {
|
||||
"$ref": "#/definitions/block_exploits"
|
||||
},
|
||||
"use_openappsec": {
|
||||
"$ref": "#/definitions/use_openappsec"
|
||||
},
|
||||
"openappsec_mode": {
|
||||
"$ref": "#/definitions/openappsec_mode"
|
||||
},
|
||||
"minimum_confidence": {
|
||||
"$ref": "#/definitions/minimum_confidence"
|
||||
},
|
||||
"caching_enabled": {
|
||||
"$ref": "#/definitions/caching_enabled"
|
||||
},
|
||||
@@ -312,6 +348,15 @@
|
||||
"block_exploits": {
|
||||
"$ref": "#/definitions/block_exploits"
|
||||
},
|
||||
"use_openappsec": {
|
||||
"$ref": "#/definitions/use_openappsec"
|
||||
},
|
||||
"openappsec_mode": {
|
||||
"$ref": "#/definitions/openappsec_mode"
|
||||
},
|
||||
"minimum_confidence": {
|
||||
"$ref": "#/definitions/minimum_confidence"
|
||||
},
|
||||
"caching_enabled": {
|
||||
"$ref": "#/definitions/caching_enabled"
|
||||
},
|
||||
|
@@ -33,6 +33,7 @@ services:
|
||||
volumes:
|
||||
- npm_data:/data
|
||||
- le_data:/etc/letsencrypt
|
||||
- ../localconfig:/ext/appsec
|
||||
- ../backend:/app
|
||||
- ../frontend:/app/frontend
|
||||
- ../global:/app/global
|
||||
|
@@ -21,6 +21,7 @@
|
||||
<meta name="msapplication-TileColor" content="#333333">
|
||||
<meta name="msapplication-config" content="/images/favicons/browserconfig.xml">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@600&display=swap" rel="stylesheet">
|
||||
<link href="/css/main.css?v=<%= version %>" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
@@ -716,6 +716,17 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
OpenappsecLog: {
|
||||
/**
|
||||
* @param {Array} [expand]
|
||||
* @param {String} [query]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getAll: function (expand, query) {
|
||||
return getAllObjects('openappsec-log', expand, query);
|
||||
}
|
||||
},
|
||||
|
||||
Reports: {
|
||||
|
||||
/**
|
||||
@@ -753,5 +764,22 @@ module.exports = {
|
||||
delete data.id;
|
||||
return fetch('put', 'settings/' + id, data);
|
||||
}
|
||||
},
|
||||
|
||||
OpenAppsecSettings: {
|
||||
/**
|
||||
* @returns {Promise}
|
||||
*/
|
||||
get: function () {
|
||||
return fetch('get', 'openappsec-settings');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} data
|
||||
* @returns {Promise}
|
||||
*/
|
||||
save: function (data) {
|
||||
return fetch('put', 'openappsec-settings', data);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@@ -407,6 +407,34 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* openappsec Log
|
||||
*/
|
||||
showOpenappsecLog: function () {
|
||||
let controller = this;
|
||||
if (Cache.User.isAdmin()) {
|
||||
require(['./main', './openappsec-log/main'], (App, View) => {
|
||||
controller.navigate('/openappsec-log');
|
||||
App.UI.showAppContent(new View());
|
||||
});
|
||||
} else {
|
||||
this.showDashboard();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* openappsec Log Metadata
|
||||
*
|
||||
* @param model
|
||||
*/
|
||||
showOpenappsecMeta: function (model) {
|
||||
if (Cache.User.isAdmin()) {
|
||||
require(['./main', './openappsec-log/meta'], function (App, View) {
|
||||
App.UI.showModalDialog(new View({model: model}));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Settings
|
||||
*/
|
||||
|
@@ -72,6 +72,7 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
@@ -90,6 +91,40 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<hr class="my-3">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" class="custom-switch-input" name="use_openappsec" value="1"<%- use_openappsec ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description font-weight-bold">open-appsec</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-6 col-form-label <%- use_openappsec ? '' : ' text-muted' %>">Enforcement Mode</label>
|
||||
<select name="openappsec_mode" class="col-sm-4 form-control custom-select" placeholder="Please Select" <%- use_openappsec ? '' : ' disabled' %>>
|
||||
<option value="detect-learn" <%- openappsec_mode === 'detect-learn' ? 'selected' : '' %>>Detect-Learn</option>
|
||||
<option value="prevent-learn" <%- openappsec_mode === 'prevent-learn' ? 'selected' : '' %>>Prevent-Learn</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-6 col-form-label <%- use_openappsec ? '' : ' text-muted' %>">Minimum confidence for prevent</label>
|
||||
<select name="minimum_confidence" class="col-sm-4 form-control custom-select" placeholder="Please Select" <%- use_openappsec ? '' : ' disabled' %>>
|
||||
<option value="critical" <%- minimum_confidence === 'critical' ? 'selected' : '' %>>Critical</option>
|
||||
<option value="high" <%- minimum_confidence === 'high' ? 'selected' : '' %>>High</option>
|
||||
<option value="medium" <%- minimum_confidence === 'medium' ? 'selected' : '' %>>Medium</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@@ -33,6 +33,9 @@ module.exports = Mn.View.extend({
|
||||
certificate_select: 'select[name="certificate_id"]',
|
||||
access_list_select: 'select[name="access_list_id"]',
|
||||
ssl_forced: 'input[name="ssl_forced"]',
|
||||
use_openappsec: 'input[name="use_openappsec"]',
|
||||
openappsec_mode: 'select[name="openappsec_mode"]',
|
||||
minimum_confidence: 'select[name="minimum_confidence"]',
|
||||
hsts_enabled: 'input[name="hsts_enabled"]',
|
||||
hsts_subdomains: 'input[name="hsts_subdomains"]',
|
||||
http2_support: 'input[name="http2_support"]',
|
||||
@@ -75,6 +78,24 @@ module.exports = Mn.View.extend({
|
||||
inputs.trigger('change');
|
||||
},
|
||||
|
||||
'change @ui.use_openappsec': function () {
|
||||
let checked = this.ui.use_openappsec.prop('checked');
|
||||
this.ui.openappsec_mode
|
||||
.prop('disabled', !checked)
|
||||
.parents('.form-group')
|
||||
.css('opacity', checked ? 1 : 0.5);
|
||||
|
||||
this.ui.minimum_confidence
|
||||
.prop('disabled', !checked)
|
||||
.parents('.form-group')
|
||||
.css('opacity', checked ? 1 : 0.5);
|
||||
|
||||
/*** check this */
|
||||
if (!checked) {
|
||||
this.ui.openappsec_mode.prop('checked', false);
|
||||
}
|
||||
},
|
||||
|
||||
'change @ui.ssl_forced': function () {
|
||||
let checked = this.ui.ssl_forced.prop('checked');
|
||||
this.ui.hsts_enabled
|
||||
@@ -146,14 +167,32 @@ module.exports = Mn.View.extend({
|
||||
}
|
||||
|
||||
let view = this;
|
||||
let data = this.ui.form.serializeJSON();
|
||||
|
||||
// Add locations
|
||||
// enable openappsec inputs for serialization. if we don't do this, serialization of custom locations will be added to the root object instead of the missing root properties with the same name.
|
||||
|
||||
let topHostDisabledElements = this.ui.form.find('#details [name*="openappsec"]:disabled, [name="minimum_confidence"]:disabled');
|
||||
topHostDisabledElements.prop('disabled', false);
|
||||
let data = this.ui.form.serializeJSON();
|
||||
topHostDisabledElements.prop('disabled', true);
|
||||
|
||||
// Check if openappsec is enabled. serializeJSON() will only return its value attribute, not its checked attribute.
|
||||
let use_openappsec = this.ui.use_openappsec.prop('checked');
|
||||
data.use_openappsec = use_openappsec;
|
||||
|
||||
// Add locations using the model defined in file frontend/js/app/nginx/proxy/location.js and the template frontend/js/app/nginx/proxy/location-item.ejs.
|
||||
// input fields with the class 'model' will automatically update the model.
|
||||
data.locations = [];
|
||||
this.locationsCollection.models.forEach((location) => {
|
||||
data.locations.push(location.toJSON());
|
||||
});
|
||||
|
||||
// convert all "false" strings to false booleans in data.
|
||||
Object.keys(data).forEach(key => {
|
||||
if (typeof data[key] === 'string' && data[key].toLowerCase() === 'false') {
|
||||
data[key] = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Serialize collects path from custom locations
|
||||
// This field must be removed from root object
|
||||
delete data.path;
|
||||
@@ -161,6 +200,7 @@ module.exports = Mn.View.extend({
|
||||
// Manipulate
|
||||
data.forward_port = parseInt(data.forward_port, 10);
|
||||
data.block_exploits = !!data.block_exploits;
|
||||
data.use_openappsec = !!data.use_openappsec;
|
||||
data.caching_enabled = !!data.caching_enabled;
|
||||
data.allow_websocket_upgrade = !!data.allow_websocket_upgrade;
|
||||
data.http2_support = !!data.http2_support;
|
||||
@@ -266,6 +306,7 @@ module.exports = Mn.View.extend({
|
||||
|
||||
this.ui.ssl_forced.trigger('change');
|
||||
this.ui.hsts_enabled.trigger('change');
|
||||
this.ui.use_openappsec.trigger('change');
|
||||
|
||||
// Domain names
|
||||
this.ui.domain_names.selectize({
|
||||
|
@@ -16,7 +16,7 @@
|
||||
<div class="col-auto">
|
||||
<div class="selectgroup">
|
||||
<label class="selectgroup-item">
|
||||
<input type="checkbox" class="selectgroup-input">
|
||||
<input id="advanced_config_toggle" type="checkbox" class="selectgroup-input">
|
||||
<span class="selectgroup-button">
|
||||
<i class="fe fe-settings"></i>
|
||||
</span>
|
||||
@@ -56,6 +56,41 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-3"/>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="custom-switch">
|
||||
<input type="checkbox" name="use_openappsec" id="use_openappsec" class="custom-switch-input" value="1"<%- use_openappsec ? ' checked' : '' %>>
|
||||
<span class="custom-switch-indicator"></span>
|
||||
<span class="custom-switch-description font-weight-bold">open-appsec</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-7 col-form-label <%- use_openappsec ? '' : ' text-muted' %>">Enforcement Mode</label>
|
||||
<select name="openappsec_mode" class="col-sm-4 form-control custom-select model" placeholder="Please Select" <%- use_openappsec ? '' : ' disabled' %>>
|
||||
<option value="detect-learn" <%- openappsec_mode === 'detect-learn' ? 'selected' : '' %>>Detect-Learn</option>
|
||||
<option value="prevent-learn" <%- openappsec_mode === 'prevent-learn' ? 'selected' : '' %>>Prevent-Learn</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-7 col-form-label <%- use_openappsec ? '' : ' text-muted' %>">Minimum confidence for prevent</label>
|
||||
<select name="minimum_confidence" class="col-sm-4 form-control custom-select model" placeholder="Please Select" <%- use_openappsec ? '' : ' disabled' %>>
|
||||
<option value="critical" <%- minimum_confidence === 'critical' ? 'selected' : '' %>>Critical</option>
|
||||
<option value="high" <%- minimum_confidence === 'high' ? 'selected' : '' %>>High</option>
|
||||
<option value="medium" <%- minimum_confidence === 'medium' ? 'selected' : '' %>>Medium</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="#" class="card-link location-delete">
|
||||
<i class="fa fa-trash"></i> <%- i18n('locations', 'delete') %>
|
||||
|
@@ -7,7 +7,10 @@ const LocationView = Mn.View.extend({
|
||||
className: 'location_block',
|
||||
|
||||
ui: {
|
||||
toggle: 'input[type="checkbox"]',
|
||||
use_openappsec: 'input[name="use_openappsec"]',
|
||||
openappsec_mode: 'select[name="openappsec_mode"]',
|
||||
minimum_confidence: 'select[name="minimum_confidence"]',
|
||||
toggle: 'input[type="checkbox"]#advanced_config_toggle',
|
||||
config: '.config',
|
||||
delete: '.location-delete'
|
||||
},
|
||||
@@ -21,6 +24,27 @@ const LocationView = Mn.View.extend({
|
||||
}
|
||||
},
|
||||
|
||||
'change @ui.use_openappsec': function () {
|
||||
let checked = this.ui.use_openappsec.prop('checked');
|
||||
this.model.set('use_openappsec', checked);
|
||||
|
||||
this.ui.openappsec_mode
|
||||
.prop('disabled', !checked)
|
||||
.parents('.form-group')
|
||||
.css('opacity', checked ? 1 : 0.5);
|
||||
|
||||
this.ui.minimum_confidence
|
||||
.prop('disabled', !checked)
|
||||
.parents('.form-group')
|
||||
.css('opacity', checked ? 1 : 0.5);
|
||||
|
||||
/*** check this */
|
||||
if (!checked) {
|
||||
this.ui.openappsec_mode.prop('checked', false);
|
||||
}
|
||||
},
|
||||
|
||||
// input fields with the class 'model' will automatically update the model.
|
||||
'change .model': function (e) {
|
||||
const map = {};
|
||||
map[e.target.name] = e.target.value;
|
||||
|
@@ -129,6 +129,7 @@ module.exports = Mn.View.extend({
|
||||
|
||||
// Manipulate
|
||||
data.block_exploits = !!data.block_exploits;
|
||||
data.use_openappsec = !!data.use_openappsec;
|
||||
data.preserve_path = !!data.preserve_path;
|
||||
data.http2_support = !!data.http2_support;
|
||||
data.hsts_enabled = !!data.hsts_enabled;
|
||||
|
@@ -13,6 +13,7 @@ module.exports = AppRouter.default.extend({
|
||||
'nginx/access': 'showNginxAccess',
|
||||
'nginx/certificates': 'showNginxCertificates',
|
||||
'audit-log': 'showAuditLog',
|
||||
'openappsec-log': 'showOpenappsecLog',
|
||||
'settings': 'showSettings',
|
||||
'*default': 'showDashboard'
|
||||
}
|
||||
|
@@ -3,12 +3,55 @@
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><%- i18n('settings', 'title') %></h3>
|
||||
</div>
|
||||
<div class="card-body no-padding min-100">
|
||||
<div class="card-body no-padding min-100 has-tabs">
|
||||
|
||||
<div class="px-4">
|
||||
<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">Nginx Proxy Manager</a></li>
|
||||
<li role="presentation" class="nav-item"><a href="#open-appsec" aria-controls="tab4" role="tab" data-toggle="tab" class="nav-link">open-appsec Advanced</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<!-- npm -->
|
||||
<div role="tabpanel" class="tab-pane active" id="details">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="dimmer active">
|
||||
<div class="loader"></div>
|
||||
<div class="dimmer-content list-region">
|
||||
<!-- List Region -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- open-appsec -->
|
||||
<div role="tabpanel" class="tab-pane" id="open-appsec">
|
||||
<div class="p-4">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label class="form-label">open-appsec Configuration File</label>
|
||||
<textarea id="local_policy" name="local_policy" rows="15" class="form-control text-monospace" placeholder="# <%- i18n('settings', 'local-policy-warning') %>"></textarea>
|
||||
</div>
|
||||
<div id="lp_success_info" class="alert alert-success success" role="alert">Local Policy Updated</div>
|
||||
<div id="lp_error_info" class="alert alert-danger" role="alert">error</div>
|
||||
<button type="button" class="btn btn-teal save"><%- i18n('str', 'save-settings') %></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -5,11 +5,18 @@ const ListView = require('./list/main');
|
||||
const ErrorView = require('../error/main');
|
||||
const template = require('./main.ejs');
|
||||
|
||||
require('jquery-serializejson');
|
||||
|
||||
module.exports = Mn.View.extend({
|
||||
id: 'settings',
|
||||
template: template,
|
||||
|
||||
ui: {
|
||||
local_policy_field: '#open-appsec form #local_policy',
|
||||
lp_success_info: '#open-appsec form #lp_success_info',
|
||||
lp_error_info: '#open-appsec form #lp_error_info',
|
||||
form: '#open-appsec form',
|
||||
save: 'button.save',
|
||||
list_region: '.list-region',
|
||||
add: '.add-item',
|
||||
dimmer: '.dimmer'
|
||||
@@ -19,9 +26,57 @@ module.exports = Mn.View.extend({
|
||||
list_region: '@ui.list_region'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click @ui.save': function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
this.ui.lp_success_info.hide();
|
||||
this.ui.lp_error_info.hide();
|
||||
|
||||
let data = this.ui.form.serializeJSON();
|
||||
console.log(data);
|
||||
App.Api.OpenAppsecSettings.save(data)
|
||||
.then(response => {
|
||||
this.showSuccess();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
this.showError(err);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
showSuccess: function () {
|
||||
this.ui.lp_success_info.show();
|
||||
setTimeout(() => {
|
||||
this.ui.lp_success_info.fadeOut();
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
showError: function (err) {
|
||||
this.ui.lp_error_info.show();
|
||||
this.ui.lp_error_info.html(err.message);
|
||||
setTimeout(() => {
|
||||
this.ui.lp_error_info.fadeOut();
|
||||
}, 3000);
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
let view = this;
|
||||
|
||||
this.ui.lp_success_info.hide();
|
||||
this.ui.lp_error_info.hide();
|
||||
|
||||
App.Api.OpenAppsecSettings.get()
|
||||
.then(response => {
|
||||
if (!view.isDestroyed() && response) {
|
||||
view.ui.local_policy_field.val(response);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
App.Api.Settings.getAll()
|
||||
.then(response => {
|
||||
if (!view.isDestroyed() && response && response.length) {
|
||||
|
@@ -6,6 +6,8 @@
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="/images/favicons/favicon-32x32.png" border="0"> <%- i18n('main', 'app') %>
|
||||
</a>
|
||||
<div class="d-flex align-items-center order-lg-2 ml-auto">
|
||||
<span class="small mr-3 text-nowrap">Secured by:</span><img src="/images/open-appsec-logo.svg" border="0" width="29" class="mr-2"> <span class="mr-sm-0 mr-md-4 my-0 align-middle text-nowrap" style="font-family: Open Sans;">open-appsec</span>
|
||||
|
||||
<div class="d-flex align-items-center order-lg-2 ml-auto">
|
||||
<div class="dropdown">
|
||||
|
@@ -42,6 +42,9 @@
|
||||
<li class="nav-item">
|
||||
<a href="/audit-log" class="nav-link"><i class="fe fe-book-open"></i> <%- i18n('audit-log', 'title') %></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/openappsec-log" class="nav-link"><i class="fe fe-book-open"></i>Security Log</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/settings" class="nav-link"><i class="fe fe-settings"></i> <%- i18n('settings', 'title') %></a>
|
||||
</li>
|
||||
|
@@ -12,6 +12,7 @@
|
||||
"roles": "Roles",
|
||||
"created-on": "Created: {date}",
|
||||
"save": "Save",
|
||||
"save-settings": "Save Settings",
|
||||
"cancel": "Cancel",
|
||||
"close": "Close",
|
||||
"enable": "Enable",
|
||||
@@ -290,7 +291,8 @@
|
||||
"default-site-404": "404 Page",
|
||||
"default-site-444": "No Response (444)",
|
||||
"default-site-html": "Custom Page",
|
||||
"default-site-redirect": "Redirect"
|
||||
"default-site-redirect": "Redirect",
|
||||
"local-policy-warning": "open-appsec YAML-based configuration file"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -10,12 +10,17 @@ const model = Backbone.Model.extend({
|
||||
advanced_config: '',
|
||||
forward_scheme: 'http',
|
||||
forward_host: '',
|
||||
forward_port: '80'
|
||||
forward_port: '80',
|
||||
use_openappsec: false,
|
||||
openappsec_mode: 'detect-learn',
|
||||
minimum_confidence: 'high'
|
||||
}
|
||||
},
|
||||
|
||||
toJSON() {
|
||||
const r = Object.assign({}, this.attributes);
|
||||
// convert use_openappsec to boolean
|
||||
r.use_openappsec = !!r.use_openappsec;
|
||||
delete r.opened;
|
||||
return r;
|
||||
},
|
||||
|
@@ -20,6 +20,9 @@ const model = Backbone.Model.extend({
|
||||
caching_enabled: false,
|
||||
allow_websocket_upgrade: false,
|
||||
block_exploits: false,
|
||||
use_openappsec: false,
|
||||
openappsec_mode: "detect-learn",
|
||||
minimum_confidence: "high",
|
||||
http2_support: false,
|
||||
advanced_config: '',
|
||||
enabled: true,
|
||||
|
Reference in New Issue
Block a user