From 81a38957bfec5d368034bb2236d375daa920152c Mon Sep 17 00:00:00 2001 From: roybarda Date: Wed, 6 Dec 2023 11:33:33 +0200 Subject: [PATCH 01/75] Update README.md --- README.md | 110 ------------------------------------------------------ 1 file changed, 110 deletions(-) diff --git a/README.md b/README.md index 0bad17f66..8b1378917 100644 --- a/README.md +++ b/README.md @@ -1,111 +1 @@ -

- -

- - - - - - - -

-This project comes as a pre-built docker image that enables you to easily forward to your websites -running at home or otherwise, including free SSL, without having to know too much about Nginx or Letsencrypt. - -- [Quick Setup](#quick-setup) -- [Full Setup](https://nginxproxymanager.com/setup/) -- [Screenshots](https://nginxproxymanager.com/screenshots/) - -## Project Goal - -I created this project to fill a personal need to provide users with a easy way to accomplish reverse -proxying hosts with SSL termination and it had to be so easy that a monkey could do it. This goal hasn't changed. -While there might be advanced options they are optional and the project should be as simple as possible -so that the barrier for entry here is low. - -Buy Me A Coffee - - -## Features - -- Beautiful and Secure Admin Interface based on [Tabler](https://tabler.github.io/) -- Easily create forwarding domains, redirections, streams and 404 hosts without knowing anything about Nginx -- Free SSL using Let's Encrypt or provide your own custom SSL certificates -- Access Lists and basic HTTP Authentication for your hosts -- Advanced Nginx configuration available for super users -- User management, permissions and audit log - - -## Hosting your home network - -I won't go in to too much detail here but here are the basics for someone new to this self-hosted world. - -1. Your home router will have a Port Forwarding section somewhere. Log in and find it -2. Add port forwarding for port 80 and 443 to the server hosting this project -3. Configure your domain name details to point to your home, either with a static ip or a service like DuckDNS or [Amazon Route53](https://github.com/jc21/route53-ddns) -4. Use the Nginx Proxy Manager as your gateway to forward to your other web based services - -## Quick Setup - -1. Install Docker and Docker-Compose - -- [Docker Install documentation](https://docs.docker.com/install/) -- [Docker-Compose Install documentation](https://docs.docker.com/compose/install/) - -2. Create a docker-compose.yml file similar to this: - -```yml -version: '3.8' -services: - app: - image: 'jc21/nginx-proxy-manager:latest' - restart: unless-stopped - ports: - - '80:80' - - '81:81' - - '443:443' - volumes: - - ./data:/data - - ./letsencrypt:/etc/letsencrypt -``` - -This is the bare minimum configuration required. See the [documentation](https://nginxproxymanager.com/setup/) for more. - -3. Bring up your stack by running - -```bash -docker-compose up -d - -# If using docker-compose-plugin -docker compose up -d - -``` - -4. Log in to the Admin UI - -When your docker container is running, connect to it on port `81` for the admin interface. -Sometimes this can take a little bit because of the entropy of keys. - -[http://127.0.0.1:81](http://127.0.0.1:81) - -Default Admin User: -``` -Email: admin@example.com -Password: changeme -``` - -Immediately after logging in with this default user you will be asked to modify your details and change your password. - - -## Contributors - -Special thanks to [all of our contributors](https://github.com/NginxProxyManager/nginx-proxy-manager/graphs/contributors). - - -## Getting Support - -1. [Found a bug?](https://github.com/NginxProxyManager/nginx-proxy-manager/issues) -2. [Discussions](https://github.com/NginxProxyManager/nginx-proxy-manager/discussions) -3. [Development Gitter](https://gitter.im/nginx-proxy-manager/community) -4. [Reddit](https://reddit.com/r/nginxproxymanager) From f9ae99ea49b1f9044c268bfcb3383cc9464812e8 Mon Sep 17 00:00:00 2001 From: roybarda Date: Wed, 6 Dec 2023 11:59:21 +0200 Subject: [PATCH 02/75] first open-appsec support --- backend/internal/proxy-host.js | 61 ++++++++++++++++++- backend/package.json | 1 + backend/routes/api/main.js | 2 + backend/schema/definitions.json | 17 ++++++ backend/schema/endpoints/proxy-hosts.json | 45 ++++++++++++++ docker/docker-compose.dev.yml | 1 + frontend/html/partials/header.ejs | 1 + frontend/js/app/api.js | 28 +++++++++ frontend/js/app/controller.js | 28 +++++++++ frontend/js/app/nginx/proxy/form.ejs | 35 +++++++++++ frontend/js/app/nginx/proxy/form.js | 45 +++++++++++++- frontend/js/app/nginx/proxy/location-item.ejs | 37 ++++++++++- frontend/js/app/nginx/proxy/location.js | 26 +++++++- frontend/js/app/nginx/redirection/form.js | 1 + frontend/js/app/router.js | 1 + frontend/js/app/settings/main.ejs | 45 +++++++++++++- frontend/js/app/settings/main.js | 55 +++++++++++++++++ frontend/js/app/ui/header/main.ejs | 2 + frontend/js/app/ui/menu/main.ejs | 3 + frontend/js/i18n/messages.json | 4 +- frontend/js/models/proxy-host-location.js | 7 ++- frontend/js/models/proxy-host.js | 3 + 22 files changed, 440 insertions(+), 8 deletions(-) diff --git a/backend/internal/proxy-host.js b/backend/internal/proxy-host.js index 02a98da26..a83413ca7 100644 --- a/backend/internal/proxy-host.js +++ b/backend/internal/proxy-host.js @@ -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); } diff --git a/backend/package.json b/backend/package.json index e8f582556..3227ec93c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -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", diff --git a/backend/routes/api/main.js b/backend/routes/api/main.js index 33cbbc21f..5eeea4517 100644 --- a/backend/routes/api/main.js +++ b/backend/routes/api/main.js @@ -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')); diff --git a/backend/schema/definitions.json b/backend/schema/definitions.json index 4b4f3405c..b3c87b57b 100644 --- a/backend/schema/definitions.json +++ b/backend/schema/definitions.json @@ -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, diff --git a/backend/schema/endpoints/proxy-hosts.json b/backend/schema/endpoints/proxy-hosts.json index 9a3fff2fc..029fd61f1 100644 --- a/backend/schema/endpoints/proxy-hosts.json +++ b/backend/schema/endpoints/proxy-hosts.json @@ -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" }, diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 6d8cf87c6..a8c68e4d0 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -33,6 +33,7 @@ services: volumes: - npm_data:/data - le_data:/etc/letsencrypt + - ../localconfig:/ext/appsec - ../backend:/app - ../frontend:/app/frontend - ../global:/app/global diff --git a/frontend/html/partials/header.ejs b/frontend/html/partials/header.ejs index cabb9df28..c2e67a449 100644 --- a/frontend/html/partials/header.ejs +++ b/frontend/html/partials/header.ejs @@ -21,6 +21,7 @@ + diff --git a/frontend/js/app/api.js b/frontend/js/app/api.js index 6e33a6dca..2c198bf54 100644 --- a/frontend/js/app/api.js +++ b/frontend/js/app/api.js @@ -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); } + }, }; diff --git a/frontend/js/app/controller.js b/frontend/js/app/controller.js index ccb2978a8..eb9677bbf 100644 --- a/frontend/js/app/controller.js +++ b/frontend/js/app/controller.js @@ -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 */ diff --git a/frontend/js/app/nginx/proxy/form.ejs b/frontend/js/app/nginx/proxy/form.ejs index 56868f552..8d0246238 100644 --- a/frontend/js/app/nginx/proxy/form.ejs +++ b/frontend/js/app/nginx/proxy/form.ejs @@ -72,6 +72,7 @@ +
+ +
+
+ +
+ +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ diff --git a/frontend/js/app/nginx/proxy/form.js b/frontend/js/app/nginx/proxy/form.js index 1dfb5c189..50b0dd30e 100644 --- a/frontend/js/app/nginx/proxy/form.js +++ b/frontend/js/app/nginx/proxy/form.js @@ -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({ diff --git a/frontend/js/app/nginx/proxy/location-item.ejs b/frontend/js/app/nginx/proxy/location-item.ejs index 466cb9bad..c0866740a 100644 --- a/frontend/js/app/nginx/proxy/location-item.ejs +++ b/frontend/js/app/nginx/proxy/location-item.ejs @@ -16,7 +16,7 @@
+
+
+
+
+ +
+
+
+ +
+
+
+ + +
+
+ +
+
+ + +
+
+
<%- i18n('locations', 'delete') %> diff --git a/frontend/js/app/nginx/proxy/location.js b/frontend/js/app/nginx/proxy/location.js index e9513a480..6e1fe9ae6 100644 --- a/frontend/js/app/nginx/proxy/location.js +++ b/frontend/js/app/nginx/proxy/location.js @@ -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; diff --git a/frontend/js/app/nginx/redirection/form.js b/frontend/js/app/nginx/redirection/form.js index 1f81feebc..d14bfd3de 100644 --- a/frontend/js/app/nginx/redirection/form.js +++ b/frontend/js/app/nginx/redirection/form.js @@ -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; diff --git a/frontend/js/app/router.js b/frontend/js/app/router.js index a036bfc57..d005e582c 100644 --- a/frontend/js/app/router.js +++ b/frontend/js/app/router.js @@ -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' } diff --git a/frontend/js/app/settings/main.ejs b/frontend/js/app/settings/main.ejs index 2b02769f2..8dc37c9fb 100644 --- a/frontend/js/app/settings/main.ejs +++ b/frontend/js/app/settings/main.ejs @@ -3,12 +3,55 @@

<%- i18n('settings', 'title') %>

-
+
+ + + +
+ + +
+
+
+ +
+
+
+ + +
+
+ +
+
+
+
+ + +
+ + + +
+
+ +
+ +
+
+ +
+
diff --git a/frontend/js/app/settings/main.js b/frontend/js/app/settings/main.js index 96b2941ff..ea7b41c2d 100644 --- a/frontend/js/app/settings/main.js +++ b/frontend/js/app/settings/main.js @@ -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) { diff --git a/frontend/js/app/ui/header/main.ejs b/frontend/js/app/ui/header/main.ejs index 18ed2b6a6..7467ba1cc 100644 --- a/frontend/js/app/ui/header/main.ejs +++ b/frontend/js/app/ui/header/main.ejs @@ -6,6 +6,8 @@   <%- i18n('main', 'app') %> +
+ Secured by: open-appsec
diff --git a/frontend/js/app/openappsec-log/main.js b/frontend/js/app/openappsec-log/main.js index 356a8646b..f25642a40 100755 --- a/frontend/js/app/openappsec-log/main.js +++ b/frontend/js/app/openappsec-log/main.js @@ -1,35 +1,99 @@ -const Mn = require('backbone.marionette'); -const App = require('../main'); +const Mn = require('backbone.marionette'); +const App = require('../main'); const OpenappsecLogModel = require('../../models/openappsec-log'); -const ListView = require('./list/main'); -const template = require('./main.ejs'); -const ErrorView = require('../error/main'); -const EmptyView = require('../empty/main'); +const ListView = require('./list/main'); +const template = require('./main.ejs'); +const ErrorView = require('../error/main'); +const EmptyView = require('../empty/main'); +const { data } = require('jquery'); +const Controller = require('../controller'); + +let PaginationView = Mn.View.extend({ + tagName: 'ul', + className: 'pagination justify-content-center mt-4 border-top pt-3', + + initialize: function (options) { + this.totalPages = parseInt(options.totalPages) || 1; + this.currentPage = parseInt(options.currentPage) || 1; + this.totalDataLines = parseInt(options.totalDataLines) || 1; + this.maxPageLinks = 15; + }, + + template: _.template( + '
  • ">' + + 'First' + + '
  • ' + + '
  • ">' + + 'Previous' + + '
  • ' + + '<% let startPage = Math.max(1, currentPage - Math.floor(maxPageLinks / 2)); %>' + + '<% let endPage = Math.min(startPage + maxPageLinks - 1, totalPages); %>' + + '<% startPage = Math.max(1, endPage - maxPageLinks + 1); %>' + + '<% for (let i = startPage; i <= endPage; i++) { %>' + + '
  • ">' + + '<%= i %>' + + '
  • ' + + '<% } %>' + + '
  • ">' + + 'Next' + + '
  • ' + + '
  • ">' + + 'Last' + + '
  • ' + + '
  • ' + + 'Total lines: <%= totalDataLines %>' + + '
  • ' + ), + + templateContext: function () { + return { + totalDataLines: this.totalDataLines, + totalPages: this.totalPages, + currentPage: this.currentPage, + maxPageLinks: this.maxPageLinks + }; + } +}); module.exports = Mn.View.extend({ - id: 'openappsec-log', + id: 'openappsec-log', template: template, + + initialize: function (options) { + this.options = options; + // this.listenTo(App, 'openappsec-log:page', this.setPage); + }, ui: { list_region: '.list-region', - dimmer: '.dimmer', - search: '.search-form', - query: 'input[name="source-query"]' + dimmer: '.dimmer', + search: '.search-form', + query: 'input[name="source-query"]', + pagination_region: '.pagination-region' }, fetch: App.Api.OpenappsecLog.getAll, - showData: function(response) { + showData: function (response) { + const totalDataLines = response.length; this.showChildView('list_region', new ListView({ - collection: new OpenappsecLogModel.Collection(response) + collection: new OpenappsecLogModel.Collection(response), + page: this.options.page, + perPage: this.options.perPage + })); + + this.showChildView('pagination_region', new PaginationView({ + totalDataLines: totalDataLines, + totalPages: Math.ceil(totalDataLines / this.options.perPage), + currentPage: this.options.page })); }, - showError: function(err) { + showError: function (err) { this.showChildView('list_region', new ErrorView({ - code: err.code, + code: err.code, message: err.message, - retry: function () { + retry: function () { App.Controller.showOpenappsecLog(); } })); @@ -37,15 +101,16 @@ module.exports = Mn.View.extend({ console.error(err); }, - showEmpty: function() { + showEmpty: function () { this.showChildView('list_region', new EmptyView({ - title: App.i18n('audit-log', 'empty'), + title: App.i18n('audit-log', 'empty'), subtitle: App.i18n('audit-log', 'empty-subtitle') })); }, regions: { - list_region: '@ui.list_region' + list_region: '@ui.list_region', + pagination_region: '@ui.pagination_region' }, events: { @@ -58,15 +123,24 @@ module.exports = Mn.View.extend({ .catch(err => { this.showError(err); }); + }, + + 'click @ui.pagination_region a': function (e) { + e.preventDefault(); + + // get the page number from the link + const newPage = $(e.currentTarget).attr('href').split('/').pop(); + Controller.navigate('/openappsec-log/page/' + newPage, { trigger: true }); } }, onRender: function () { let view = this; + let query = this.ui.query.val() || ''; - view.fetch(['user']) + view.fetch(['user'], query) .then(response => { - if (!view.isDestroyed() && response && response.length) { + if (!view.isDestroyed() && response) { view.showData(response); } else { view.showEmpty(); diff --git a/frontend/js/app/router.js b/frontend/js/app/router.js index d005e582c..e782623c9 100644 --- a/frontend/js/app/router.js +++ b/frontend/js/app/router.js @@ -13,7 +13,8 @@ module.exports = AppRouter.default.extend({ 'nginx/access': 'showNginxAccess', 'nginx/certificates': 'showNginxCertificates', 'audit-log': 'showAuditLog', - 'openappsec-log': 'showOpenappsecLog', + 'openappsec-log': 'showOpenappsecLogPage', + 'openappsec-log/page/:number': 'showOpenappsecLogPage', 'settings': 'showSettings', '*default': 'showDashboard' } From dbd78e5e039098815e9898d5271cd8dc50359b43 Mon Sep 17 00:00:00 2001 From: Rami Winestock Date: Wed, 13 Dec 2023 17:40:51 +0200 Subject: [PATCH 09/75] Filtered log views --- backend/internal/openappsec-log.js | 86 +-------- .../js/app/openappsec-log/list-all/item.ejs | 43 +++++ .../js/app/openappsec-log/list-all/item.js | 32 ++++ .../js/app/openappsec-log/list-all/main.ejs | 22 +++ .../js/app/openappsec-log/list-all/main.js | 60 ++++++ .../openappsec-log/list-important/item.ejs | 43 +++++ .../app/openappsec-log/list-important/item.js | 32 ++++ .../openappsec-log/list-important/main.ejs | 22 +++ .../app/openappsec-log/list-important/main.js | 43 +++++ .../list-notifications/item.ejs | 32 ++++ .../openappsec-log/list-notifications/item.js | 32 ++++ .../list-notifications/main.ejs | 13 ++ .../openappsec-log/list-notifications/main.js | 43 +++++ frontend/js/app/openappsec-log/list/main.js | 6 - frontend/js/app/openappsec-log/main.ejs | 33 +++- frontend/js/app/openappsec-log/main.js | 172 ++++++++++++++---- frontend/js/app/openappsec-log/pagination.ejs | 23 +++ frontend/js/app/settings/main.ejs | 2 +- frontend/js/models/openappsec-log.js | 25 ++- 19 files changed, 627 insertions(+), 137 deletions(-) create mode 100644 frontend/js/app/openappsec-log/list-all/item.ejs create mode 100644 frontend/js/app/openappsec-log/list-all/item.js create mode 100644 frontend/js/app/openappsec-log/list-all/main.ejs create mode 100644 frontend/js/app/openappsec-log/list-all/main.js create mode 100644 frontend/js/app/openappsec-log/list-important/item.ejs create mode 100644 frontend/js/app/openappsec-log/list-important/item.js create mode 100644 frontend/js/app/openappsec-log/list-important/main.ejs create mode 100644 frontend/js/app/openappsec-log/list-important/main.js create mode 100644 frontend/js/app/openappsec-log/list-notifications/item.ejs create mode 100644 frontend/js/app/openappsec-log/list-notifications/item.js create mode 100644 frontend/js/app/openappsec-log/list-notifications/main.ejs create mode 100644 frontend/js/app/openappsec-log/list-notifications/main.js create mode 100644 frontend/js/app/openappsec-log/pagination.ejs diff --git a/backend/internal/openappsec-log.js b/backend/internal/openappsec-log.js index 22def5c3d..74a23adc1 100755 --- a/backend/internal/openappsec-log.js +++ b/backend/internal/openappsec-log.js @@ -6,86 +6,6 @@ const { APPSEC_LOG_DIR } = require('../lib/constants'); const internalOpenappsecLog = { - /** - * All logs - * - * @param {Access} access - * @param {Array} [expand] - * @param {String} [search_query] - * @returns {Promise} - */ - getAllold: (access, expand, search_query) => { - return access.can('auditlog:list') - .then(() => { - - const directoryPath = APPSEC_LOG_DIR; - - const readdir = util.promisify(fs.readdir); - const readFile = util.promisify(fs.readFile); - - async function listLogFiles(dir) { - const files = await readdir(dir); - const logFiles = files.filter(file => path.extname(file).startsWith('.log')); - - const sortedLogFiles = logFiles.sort((a, b) => { - const baseA = path.basename(a, path.extname(a)); - const baseB = path.basename(b, path.extname(b)); - - if (baseA < baseB) return -1; - if (baseA > baseB) return 1; - - return path.extname(a).localeCompare(path.extname(b)); - }); - - // Group the log files by their base name - const groupedFiles = sortedLogFiles.reduce((groups, file) => { - const fileName = path.basename(file, path.extname(file)); - if (!groups[fileName]) { - groups[fileName] = []; - } - groups[fileName].push(file); - return groups; - }, {}); - - const wrappedObjects = []; - - for (const [groupName, files] of Object.entries(groupedFiles)) { - for (const file of files) { - try { - const content = await readFile(path.join(dir, file), 'utf8'); - const lines = content.split('\n'); - for (const line of lines) { - try { - const json = JSON.parse(line); - const wrappedObject = { - source: groupName, - meta: json, - serviceName: json.eventSource.serviceName, - eventPriority: json.eventPriority, - eventSeverity: json.eventSeverity, - eventLevel: json.eventLevel, - eventTime: json.eventTime, - eventName: json.eventName - }; - wrappedObjects.push(wrappedObject); - } catch (err) { - // Ignore lines that don't contain JSON data - } - } - } catch (err) { - console.error(`Failed to read file ${file}: ${err.message}`); - } - } - } - wrappedObjects.sort((a, b) => new Date(b.eventTime) - new Date(a.eventTime)); - return wrappedObjects; - } - - let groupedFiles = listLogFiles(directoryPath).catch(console.error); - return groupedFiles; - }); - }, - countTotalLines: async function (directoryPath) { const files = await fs.promises.readdir(directoryPath); const logFiles = files.filter(file => path.extname(file).startsWith('.log')); @@ -153,7 +73,7 @@ const internalOpenappsecLog = { getAll: function (access, expand, search_query) { return access.can('auditlog:list') .then(async () => { - const directoryPath = '/app/openappsec_files/logs'; + const directoryPath = APPSEC_LOG_DIR; const files = await fs.promises.readdir(directoryPath); const logFiles = files.filter(file => path.extname(file).startsWith('.log')); @@ -175,12 +95,10 @@ const internalOpenappsecLog = { }); }, - getPage: function (access, expand, search_query, page, perPage) { return access.can('auditlog:list') .then(async () => { - const directoryPath = '/app/openappsec_files/logs'; - + const directoryPath = APPSEC_LOG_DIR; let totalDataLines = await this.countTotalLines(directoryPath); console.log("totalLineCount: " + totalDataLines); diff --git a/frontend/js/app/openappsec-log/list-all/item.ejs b/frontend/js/app/openappsec-log/list-all/item.ejs new file mode 100644 index 000000000..ecc0214cd --- /dev/null +++ b/frontend/js/app/openappsec-log/list-all/item.ejs @@ -0,0 +1,43 @@ + + + <%- formatDbDate(eventTime, 'D-M-YYYY, H:mm') %> + + +
    + <% var sevirityClass = 'bg-success'; + switch (eventSeverity) { + case 'Critical': + sevirityClass = 'bg-danger'; + break; + case 'Warning': + sevirityClass = 'bg-warning'; + break; + case 'Info': + sevirityClass = 'bg-success'; + //sevirityClass = 'bg-info'; + break; + case 'Debug': + sevirityClass = 'bg-success'; + break; + } + %> + <%- eventSeverity %> +
    + +<%- assetName %> +<%- securityAction %> +<%- waapIncidentType %> +<%- httpSourceId %> +<%- sourceIp %> +<%- proxyIp %> +<%- httpHostName %> +<%- httpMethod %> +<%- httpResponseCode %> +<%- httpUriPath %> +<%- protectionName %> +<%- matchedLocation %> +<%- matchedParameter %> +<%- matchedSample %> + + open + diff --git a/frontend/js/app/openappsec-log/list-all/item.js b/frontend/js/app/openappsec-log/list-all/item.js new file mode 100644 index 000000000..009e3a3e6 --- /dev/null +++ b/frontend/js/app/openappsec-log/list-all/item.js @@ -0,0 +1,32 @@ +const Mn = require('backbone.marionette'); +const Controller = require('../../controller'); +const template = require('./item.ejs'); + +module.exports = Mn.View.extend({ + template: template, + tagName: 'tr', + + ui: { + meta: 'a.meta' + }, + + events: { + 'click @ui.meta': function (e) { + e.preventDefault(); + Controller.showOpenappsecMeta(this.model); + } + }, + + templateContext: { + more: function() { + switch (this.object_type) { + case 'redirection-host': + case 'stream': + case 'proxy-host': + return this.meta.domain_names.join(', '); + } + + return '#' + (this.object_id || '?'); + } + } +}); diff --git a/frontend/js/app/openappsec-log/list-all/main.ejs b/frontend/js/app/openappsec-log/list-all/main.ejs new file mode 100644 index 000000000..184cddd4b --- /dev/null +++ b/frontend/js/app/openappsec-log/list-all/main.ejs @@ -0,0 +1,22 @@ + + Time + Event Severity + Asset Name + Security Action + AppSec Incident Type + Source Identifier + Source IP + Proxy IP + HTTP Host + HTTP Method + HTTP Response Code + HTTP URI Path + Protection Name + Matched Location + Matched Parameter + Matched Sample +   + + + + diff --git a/frontend/js/app/openappsec-log/list-all/main.js b/frontend/js/app/openappsec-log/list-all/main.js new file mode 100644 index 000000000..777d2db76 --- /dev/null +++ b/frontend/js/app/openappsec-log/list-all/main.js @@ -0,0 +1,60 @@ +const Mn = require('backbone.marionette'); +const ItemView = require('./item'); +const template = require('./main.ejs'); + +let TableBody = Mn.CollectionView.extend({ + tagName: 'tbody', + childView: ItemView, + + initialize: function (options) { + this.options = new Backbone.Model(options); + console.log("options: ", options); + // this.page = options.page; + // this.perPage = options.perPage; + this.updatePage(); + // this.listenTo(this.options, 'change:page', this.updatePage); + }, + + updatePage: function () { + let perPage = this.perPage || this.collection.length; + let page = this.page || 1; + let models; + if (this.perPage && this.page) { + console.log('updatePage2'); + models = this.collection.models.slice((page - 1) * perPage, page * perPage); + } else { + console.log('updatePage3'); + + models = this.collection.models; + } + this.collection.reset(models); + } + + // updatePage: function () { + // let perPage = this.perPage || this.collection.length; + // let page = this.page || 1; + // let models = this.collection.models.slice((page - 1) * perPage, page * perPage); + // this.collection.reset(models); + // } +}); + +module.exports = Mn.View.extend({ + tagName: 'table', + className: 'table table-hover table-outline table-vcenter card-table', + template: template, + + regions: { + body: { + el: 'tbody', + replaceElement: true + } + }, + + onRender: function () { + this.showChildView('body', new TableBody({ + collection: this.collection, + // page: this.options.page, + // perPage: this.options.perPage + })); + } +}); diff --git a/frontend/js/app/openappsec-log/list-important/item.ejs b/frontend/js/app/openappsec-log/list-important/item.ejs new file mode 100644 index 000000000..5537babb6 --- /dev/null +++ b/frontend/js/app/openappsec-log/list-important/item.ejs @@ -0,0 +1,43 @@ + + + <%- formatDbDate(eventTime, 'D-M-YY, H:mm') %> + + +
    + <% var sevirityClass = 'bg-success'; + switch (eventSeverity) { + case 'Critical': + sevirityClass = 'bg-danger'; + break; + case 'Warning': + sevirityClass = 'bg-warning'; + break; + case 'Info': + sevirityClass = 'bg-success'; + //sevirityClass = 'bg-info'; + break; + case 'Debug': + sevirityClass = 'bg-success'; + break; + } + %> + <%- eventSeverity %> +
    + +<%- assetName %> +<%- securityAction %> +<%- waapIncidentType %> +<%- httpSourceId %> +<%- sourceIp %> +<%- proxyIp %> +<%- httpHostName %> +<%- httpMethod %> +<%- httpResponseCode %> +<%- httpUriPath %> +<%- protectionName %> +<%- matchedLocation %> +<%- matchedParameter %> +<%- matchedSample %> + + open + diff --git a/frontend/js/app/openappsec-log/list-important/item.js b/frontend/js/app/openappsec-log/list-important/item.js new file mode 100644 index 000000000..009e3a3e6 --- /dev/null +++ b/frontend/js/app/openappsec-log/list-important/item.js @@ -0,0 +1,32 @@ +const Mn = require('backbone.marionette'); +const Controller = require('../../controller'); +const template = require('./item.ejs'); + +module.exports = Mn.View.extend({ + template: template, + tagName: 'tr', + + ui: { + meta: 'a.meta' + }, + + events: { + 'click @ui.meta': function (e) { + e.preventDefault(); + Controller.showOpenappsecMeta(this.model); + } + }, + + templateContext: { + more: function() { + switch (this.object_type) { + case 'redirection-host': + case 'stream': + case 'proxy-host': + return this.meta.domain_names.join(', '); + } + + return '#' + (this.object_id || '?'); + } + } +}); diff --git a/frontend/js/app/openappsec-log/list-important/main.ejs b/frontend/js/app/openappsec-log/list-important/main.ejs new file mode 100644 index 000000000..184cddd4b --- /dev/null +++ b/frontend/js/app/openappsec-log/list-important/main.ejs @@ -0,0 +1,22 @@ + + Time + Event Severity + Asset Name + Security Action + AppSec Incident Type + Source Identifier + Source IP + Proxy IP + HTTP Host + HTTP Method + HTTP Response Code + HTTP URI Path + Protection Name + Matched Location + Matched Parameter + Matched Sample +   + + + + diff --git a/frontend/js/app/openappsec-log/list-important/main.js b/frontend/js/app/openappsec-log/list-important/main.js new file mode 100644 index 000000000..0c90551ba --- /dev/null +++ b/frontend/js/app/openappsec-log/list-important/main.js @@ -0,0 +1,43 @@ +const Mn = require('backbone.marionette'); +const ItemView = require('./item'); +const template = require('./main.ejs'); + +let TableBody = Mn.CollectionView.extend({ + tagName: 'tbody', + childView: ItemView, + + initialize: function (options) { + this.options = new Backbone.Model(options); + this.page = options.page; + this.perPage = options.perPage; + this.updatePage(); + this.listenTo(this.options, 'change:page', this.updatePage); + }, + + updatePage: function () { + console.log('updatePage'); + let models = this.collection.models.slice((this.page - 1) * this.perPage, this.page * this.perPage); + this.collection.reset(models); + } +}); + +module.exports = Mn.View.extend({ + tagName: 'table', + className: 'table table-hover table-outline table-vcenter card-table', + template: template, + + regions: { + body: { + el: 'tbody', + replaceElement: true + } + }, + + onRender: function () { + this.showChildView('body', new TableBody({ + collection: this.collection, + page: this.options.page, + perPage: this.options.perPage + })); + } +}); diff --git a/frontend/js/app/openappsec-log/list-notifications/item.ejs b/frontend/js/app/openappsec-log/list-notifications/item.ejs new file mode 100644 index 000000000..e901a23e7 --- /dev/null +++ b/frontend/js/app/openappsec-log/list-notifications/item.ejs @@ -0,0 +1,32 @@ + +<%- formatDbDate(eventTime, 'D-M-YY, H:mm') %> + +
    + <% var sevirityClass = 'bg-success'; + switch (eventSeverity) { + case 'Critical': + sevirityClass = 'bg-danger'; + break; + case 'Warning': + sevirityClass = 'bg-warning'; + break; + case 'Info': + sevirityClass = 'bg-success'; + //sevirityClass = 'bg-info'; + break; + case 'Debug': + sevirityClass = 'bg-success'; + break; + } + %> + <%- eventSeverity %> +
    + +<%- eventPriority %> +<%- eventTopic %> +<%- eventName %> +<%- suggestedRemediation %> +<%- assetName %> + + open + diff --git a/frontend/js/app/openappsec-log/list-notifications/item.js b/frontend/js/app/openappsec-log/list-notifications/item.js new file mode 100644 index 000000000..009e3a3e6 --- /dev/null +++ b/frontend/js/app/openappsec-log/list-notifications/item.js @@ -0,0 +1,32 @@ +const Mn = require('backbone.marionette'); +const Controller = require('../../controller'); +const template = require('./item.ejs'); + +module.exports = Mn.View.extend({ + template: template, + tagName: 'tr', + + ui: { + meta: 'a.meta' + }, + + events: { + 'click @ui.meta': function (e) { + e.preventDefault(); + Controller.showOpenappsecMeta(this.model); + } + }, + + templateContext: { + more: function() { + switch (this.object_type) { + case 'redirection-host': + case 'stream': + case 'proxy-host': + return this.meta.domain_names.join(', '); + } + + return '#' + (this.object_id || '?'); + } + } +}); diff --git a/frontend/js/app/openappsec-log/list-notifications/main.ejs b/frontend/js/app/openappsec-log/list-notifications/main.ejs new file mode 100644 index 000000000..9092a11ae --- /dev/null +++ b/frontend/js/app/openappsec-log/list-notifications/main.ejs @@ -0,0 +1,13 @@ + + Time + Event Severity + Event Priority + Event Topic + Event Name + Suggested Remediation if Applicable + Asset Name +   + + + + \ No newline at end of file diff --git a/frontend/js/app/openappsec-log/list-notifications/main.js b/frontend/js/app/openappsec-log/list-notifications/main.js new file mode 100644 index 000000000..0c90551ba --- /dev/null +++ b/frontend/js/app/openappsec-log/list-notifications/main.js @@ -0,0 +1,43 @@ +const Mn = require('backbone.marionette'); +const ItemView = require('./item'); +const template = require('./main.ejs'); + +let TableBody = Mn.CollectionView.extend({ + tagName: 'tbody', + childView: ItemView, + + initialize: function (options) { + this.options = new Backbone.Model(options); + this.page = options.page; + this.perPage = options.perPage; + this.updatePage(); + this.listenTo(this.options, 'change:page', this.updatePage); + }, + + updatePage: function () { + console.log('updatePage'); + let models = this.collection.models.slice((this.page - 1) * this.perPage, this.page * this.perPage); + this.collection.reset(models); + } +}); + +module.exports = Mn.View.extend({ + tagName: 'table', + className: 'table table-hover table-outline table-vcenter card-table', + template: template, + + regions: { + body: { + el: 'tbody', + replaceElement: true + } + }, + + onRender: function () { + this.showChildView('body', new TableBody({ + collection: this.collection, + page: this.options.page, + perPage: this.options.perPage + })); + } +}); diff --git a/frontend/js/app/openappsec-log/list/main.js b/frontend/js/app/openappsec-log/list/main.js index 01f5edd46..a7ef3281c 100755 --- a/frontend/js/app/openappsec-log/list/main.js +++ b/frontend/js/app/openappsec-log/list/main.js @@ -14,12 +14,6 @@ let TableBody = Mn.CollectionView.extend({ this.listenTo(this.options, 'change:page', this.updatePage); }, - setPage: function (page) { - this.page = page; - this.updatePage(); - this.render(); - }, - updatePage: function () { let models = this.collection.models.slice((this.page - 1) * this.perPage, this.page * this.perPage); this.collection.reset(models); diff --git a/frontend/js/app/openappsec-log/main.ejs b/frontend/js/app/openappsec-log/main.ejs index 81c0cabc2..4beee89c0 100755 --- a/frontend/js/app/openappsec-log/main.ejs +++ b/frontend/js/app/openappsec-log/main.ejs @@ -13,14 +13,31 @@ -->
    -
    -
    -
    -
    - -
    -
    - +
    + + +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    diff --git a/frontend/js/app/openappsec-log/main.js b/frontend/js/app/openappsec-log/main.js index f25642a40..8f2ecad6b 100755 --- a/frontend/js/app/openappsec-log/main.js +++ b/frontend/js/app/openappsec-log/main.js @@ -1,8 +1,11 @@ const Mn = require('backbone.marionette'); const App = require('../main'); const OpenappsecLogModel = require('../../models/openappsec-log'); -const ListView = require('./list/main'); +const ListViewTab1 = require('./list-important/main'); +const ListViewTab2 = require('./list-all/main'); +const ListViewTab3 = require('./list-notifications/main'); const template = require('./main.ejs'); +const paginationTemplate = require('./pagination.ejs'); const ErrorView = require('../error/main'); const EmptyView = require('../empty/main'); const { data } = require('jquery'); @@ -19,31 +22,7 @@ let PaginationView = Mn.View.extend({ this.maxPageLinks = 15; }, - template: _.template( - '
  • ">' + - 'First' + - '
  • ' + - '
  • ">' + - 'Previous' + - '
  • ' + - '<% let startPage = Math.max(1, currentPage - Math.floor(maxPageLinks / 2)); %>' + - '<% let endPage = Math.min(startPage + maxPageLinks - 1, totalPages); %>' + - '<% startPage = Math.max(1, endPage - maxPageLinks + 1); %>' + - '<% for (let i = startPage; i <= endPage; i++) { %>' + - '
  • ">' + - '<%= i %>' + - '
  • ' + - '<% } %>' + - '
  • ">' + - 'Next' + - '
  • ' + - '
  • ">' + - 'Last' + - '
  • ' + - '
  • ' + - 'Total lines: <%= totalDataLines %>' + - '
  • ' - ), + template: paginationTemplate, templateContext: function () { return { @@ -61,7 +40,6 @@ module.exports = Mn.View.extend({ initialize: function (options) { this.options = options; - // this.listenTo(App, 'openappsec-log:page', this.setPage); }, ui: { @@ -74,19 +52,84 @@ module.exports = Mn.View.extend({ fetch: App.Api.OpenappsecLog.getAll, - showData: function (response) { - const totalDataLines = response.length; - this.showChildView('list_region', new ListView({ - collection: new OpenappsecLogModel.Collection(response), - page: this.options.page, + showData: function (response, tab = 'tab1') { + + // Filter the response data for each tab + const eventSeverities = ["critical", "high"]; + const tab1Data = response.filter(item => eventSeverities.includes(item.eventSeverity.trim().toLowerCase())); + + const eventNames = ["waap telemetry", "waap attack type telemetry", "ips stats"]; + const tab2Data = response.filter(item => !eventNames.includes(item.eventName.trim().toLowerCase())); + + const eventLevels = ["action item"]; + const tab3Data = response.filter(item => eventLevels.includes(item.eventLevel.trim().toLowerCase())); + + // Store the lengths of the original collections + this.tabCollectionLengths = { + tab1: response.length, + tab2: response.length, + tab3: response.length + }; + + this.tabCollections = { + tab1: new OpenappsecLogModel.Collection(tab1Data), + tab2: new OpenappsecLogModel.Collection(tab2Data), + tab3: new OpenappsecLogModel.Collection(tab3Data) + }; + + this.tabPaginationStates = { + tab1: { page: 1, perPage: this.options.perPage }, + tab2: { page: 1, perPage: this.options.perPage }, + tab3: { page: 1, perPage: this.options.perPage } + }; + + // Define an object mapping for the ListViews + const listViewMapping = { + tab1: ListViewTab1, + tab2: ListViewTab2, + tab3: ListViewTab3 + }; + + // Get the current tab + const currentTab = tab; // Replace this with the actual current tab + + // Select the ListView for the current tab + const CurrentListView = listViewMapping[currentTab]; + + // Show the ListView for the current tab + this.showChildView('list_region', new CurrentListView({ + collection: this.tabCollections[currentTab], + page: 1, perPage: this.options.perPage + // page: this.tabPaginationStates[currentTab].page, + // perPage: this.tabPaginationStates[currentTab].perPage })); - this.showChildView('pagination_region', new PaginationView({ - totalDataLines: totalDataLines, - totalPages: Math.ceil(totalDataLines / this.options.perPage), - currentPage: this.options.page - })); + // const totalDataLines = response.length; + // this.showChildView('list_region', new ListView({ + // collection: this.tabCollections.tab1, + // page: this.tabPaginationStates.tab1.page, + // perPage: this.tabPaginationStates.tab1.perPage + // })); + + // this.showChildView('pagination_region', new PaginationView({ + // totalDataLines: this.tabCollectionLengths.tab1, + // totalPages: Math.ceil(this.tabCollectionLengths.tab1 / this.options.perPage), + // currentPage: this.tabPaginationStates.tab1.page + // })); + + // const totalDataLines = response.length; + // this.showChildView('list_region', new ListView({ + // collection: new OpenappsecLogModel.Collection(response), + // page: this.options.page, + // perPage: this.options.perPage + // })); + + // this.showChildView('pagination_region', new PaginationView({ + // totalDataLines: totalDataLines, + // totalPages: Math.ceil(totalDataLines / this.options.perPage), + // currentPage: this.options.page + // })); }, showError: function (err) { @@ -125,6 +168,8 @@ module.exports = Mn.View.extend({ }); }, + 'click .nav-link': 'onTabClick', + 'click @ui.pagination_region a': function (e) { e.preventDefault(); @@ -134,6 +179,59 @@ module.exports = Mn.View.extend({ } }, + onTabClick: function(event) { + event.preventDefault(); + const selectedTab = event.target.id; + + console.log("selectedTab: ", selectedTab); + + // let ListView; + // switch (selectedTab) { + // case 'tab1': + // ListView = ListViewTab1; + // break; + // case 'tab2': + // ListView = ListViewTab2; + // break; + // case 'tab3': + // ListView = ListViewTab3; + // break; + // default: + // ListView = ListViewTab1; + // } + + let view = this; + let query = this.ui.query.val() || ''; + + view.fetch(['user'], query) + .then(response => { + if (!view.isDestroyed() && response) { + view.showData(response, selectedTab); + } else { + view.showEmpty(); + } + }) + .catch(err => { + view.showError(err); + }) + .then(() => { + view.ui.dimmer.removeClass('active'); + }); + + // this.showChildView('list_region', new ListView({ + // collection: this.tabCollections[selectedTab], + // page: this.tabPaginationStates[selectedTab].page, + // perPage: this.tabPaginationStates[selectedTab].perPage + // })); + + // this.showChildView('pagination_region', new PaginationView({ + // totalDataLines: this.tabCollections[selectedTab].length, + // totalPages: Math.ceil(this.tabCollections[selectedTab].length / this.options.perPage), + // currentPage: this.tabPaginationStates[selectedTab].page + // })); + + }, + onRender: function () { let view = this; let query = this.ui.query.val() || ''; diff --git a/frontend/js/app/openappsec-log/pagination.ejs b/frontend/js/app/openappsec-log/pagination.ejs new file mode 100644 index 000000000..a4885b2e0 --- /dev/null +++ b/frontend/js/app/openappsec-log/pagination.ejs @@ -0,0 +1,23 @@ +
  • "> + First +
  • +
  • "> + Previous +
  • +<% let startPage = Math.max(1, currentPage - Math.floor(maxPageLinks / 2)); %> +<% let endPage = Math.min(startPage + maxPageLinks - 1, totalPages); %> +<% startPage = Math.max(1, endPage - maxPageLinks + 1); %> +<% for (let i = startPage; i <= endPage; i++) { %> +
  • "> + <%= i %> +
  • +<% } %> +
  • "> + Next +
  • +
  • "> + Last +
  • +
  • + Total lines: <%= totalDataLines %> +
  • \ No newline at end of file diff --git a/frontend/js/app/settings/main.ejs b/frontend/js/app/settings/main.ejs index 8dc37c9fb..2031a4fee 100644 --- a/frontend/js/app/settings/main.ejs +++ b/frontend/js/app/settings/main.ejs @@ -8,7 +8,7 @@ diff --git a/frontend/js/models/openappsec-log.js b/frontend/js/models/openappsec-log.js index c929a0bd8..5f0575389 100755 --- a/frontend/js/models/openappsec-log.js +++ b/frontend/js/models/openappsec-log.js @@ -5,7 +5,30 @@ const model = Backbone.Model.extend({ defaults: function () { return { - name: '' + name: '-', + eventSeverity: '-', + assetName: '-', + securityAction: '-', + waapIncidentType: '-', + httpSourceId: '-', + sourceIp: '-', + // 'Proxy-IP': '-', + proxyIp: '-', + httpHostName: '-', + httpMethod: '-', + // 'HTTP-Response-Code': '-', + httpResponseCode: '-', + httpUriPath: '-', + // 'Protection-Name': '-', + protectionName: '-', + matchedLocation: '-', + matchedParameter: '-', + matchedSample: '-', + eventPriority: '-', + eventTopic: '-', + eventName: '-', + // Suggested Remediation if Applicable + suggestedRemediation: '-' }; } }); From ce84a865f15a09df5e8e1599a7bd1069f76d740f Mon Sep 17 00:00:00 2001 From: Rami Winestock Date: Wed, 13 Dec 2023 21:51:43 +0200 Subject: [PATCH 10/75] Notify policy updates to open-appsec container. UI labels in log. --- backend/internal/nginx-openappsec.js | 69 ++++++++++++++++++- backend/internal/openappsec-log.js | 1 - backend/internal/proxy-host.js | 29 +++++--- backend/lib/constants.js | 4 ++ .../js/app/openappsec-log/list-all/item.ejs | 31 ++------- .../js/app/openappsec-log/list-all/main.ejs | 2 +- .../js/app/openappsec-log/list-all/main.js | 11 --- .../openappsec-log/list-important/item.ejs | 29 +------- .../openappsec-log/list-important/main.ejs | 2 +- .../app/openappsec-log/list-important/main.js | 1 - .../list-notifications/item.ejs | 24 +------ .../openappsec-log/list-notifications/main.js | 1 - frontend/js/app/openappsec-log/main.js | 18 ----- 13 files changed, 102 insertions(+), 120 deletions(-) diff --git a/backend/internal/nginx-openappsec.js b/backend/internal/nginx-openappsec.js index a8806156a..1520fbd22 100755 --- a/backend/internal/nginx-openappsec.js +++ b/backend/internal/nginx-openappsec.js @@ -1,3 +1,6 @@ +const util = require('util'); +const execPromise = util.promisify(require('child_process').exec); +const { exec } = require('child_process'); const _ = require('lodash'); const fs = require('fs'); const logger = require('../logger').nginx; @@ -100,7 +103,24 @@ const internalNginxOpenappsec = { (err) => { logger.error('Error generating openappsec config:', err); return Promise.reject(err); + }) + .then(() => { + // Return the notifyPolicyUpdate promise chain + // notify openappsec to apply the policy + return internalNginxOpenappsec.notifyPolicyUpdate().catch((errorMessage) => { + console.error('Error:', errorMessage); + const errorMessageForUI = `Error: Policy couldn’t be applied, open-appsec-agent container is not responding. + Check if open-appec-agent container is running, then apply open-appsec Configuration + again by clicking here: +
    Settings -> open-appsec Advanced -> Save Settings`; + + return Promise.reject(new Error(errorMessageForUI)); }); + }) + .catch((err) => { + logger.error('Error generating openappsec config:', err); + throw err; // Propagate the error to the caller + }); }, /** @@ -122,9 +142,22 @@ const internalNginxOpenappsec = { internalNginxOpenappsec.removeMatchingNodes(openappsecConfig, pattern); fs.writeFileSync(configFilePath, yaml.dump(openappsecConfig)); }) - .catch(err => { + .then(() => { + // Return the notifyPolicyUpdate promise chain + // notify openappsec to apply the policy + return internalNginxOpenappsec.notifyPolicyUpdate().catch((errorMessage) => { + console.error('---Error:', errorMessage); + const errorMessageForUI = `Error: Policy couldn’t be applied, open-appsec-agent container is not responding. + Check if open-appec-agent container is running, then apply open-appsec Configuration + again by clicking here: +
    Settings -> open-appsec Advanced -> Save Settings`; + + return Promise.reject(new Error(errorMessageForUI)); + }); + }) + .catch((err) => { logger.error('Error deleting openappsec config:', err); - return Promise.reject(err); + throw err; // Propagate the error to the caller }); }, @@ -180,6 +213,38 @@ const internalNginxOpenappsec = { } }, + notifyPolicyUpdate: async function() { + if (!constants.USE_NOTIFY_POLICY) { + console.log('USE_NOTIFY_POLICY is false'); + return; + } + let ports = constants.PORTS; + console.log(`Notifying openappsec to apply the policy on ports ${ports}`); + let lastError = null; + + for (let port of ports) { + try { + const command = `curl -s -o /dev/null -w "%{http_code}" ${constants.HOSTURL}:${port}/openappsec/apply-policy`; + console.log(`command: ${command}`); + let { stdout } = await execPromise(command); + if (stdout === '200') { + console.log(`Policy applied successfully on port ${port}`); + return; + } else { + console.log(`Policy Unexpected response code: ${stdout}`); + lastError = new Error(`Unexpected response code: ${stdout}`); + } + } catch (error) { + console.log(`Error notifying openappsec to apply the policy on port ${port}: ${error.message}`); + lastError = error; + } + } + + if (lastError) { + throw lastError; + } + }, + /** * Recursively removes nodes from a JavaScript object based on a pattern. * diff --git a/backend/internal/openappsec-log.js b/backend/internal/openappsec-log.js index 74a23adc1..dfdf19e5a 100755 --- a/backend/internal/openappsec-log.js +++ b/backend/internal/openappsec-log.js @@ -100,7 +100,6 @@ const internalOpenappsecLog = { .then(async () => { const directoryPath = APPSEC_LOG_DIR; let totalDataLines = await this.countTotalLines(directoryPath); - console.log("totalLineCount: " + totalDataLines); const files = await fs.promises.readdir(directoryPath); const logFiles = files.filter(file => path.extname(file).startsWith('.log')); diff --git a/backend/internal/proxy-host.js b/backend/internal/proxy-host.js index a83413ca7..f75607a44 100644 --- a/backend/internal/proxy-host.js +++ b/backend/internal/proxy-host.js @@ -95,9 +95,14 @@ const internalProxyHost = { }); }) .then(row => { - internalNginxOpenappsec.generateConfig(access, row, data) - return row; - }) + return internalNginxOpenappsec.generateConfig(access, row, data) + .then(() => { + return row; + }) + .catch((err) => { + throw new error.ConfigurationError(err.message); + }); + }) .then((row) => { // Audit log data.meta = _.assign({}, data.meta || {}, row.meta); @@ -174,10 +179,14 @@ const internalProxyHost = { } }) .then(row => { - internalNginxOpenappsec.generateConfig(access, row, data); - // internalNginxOpenappsec.updateConfig(row, data) - return row; - }) + return internalNginxOpenappsec.generateConfig(access, row, data) + .then(() => { + return row; + }) + .catch((err) => { + throw new error.ConfigurationError(err.message); + }); + }) .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({}, { @@ -316,7 +325,11 @@ const internalProxyHost = { }) .then(() => { // Delete openappsec config - internalNginxOpenappsec.deleteConfig(access, row); + return internalNginxOpenappsec.deleteConfig(access, row) + .catch((err) => { + throw new error.ConfigurationError(err.message); + }); + }) .then(() => { // Delete Nginx Config diff --git a/backend/lib/constants.js b/backend/lib/constants.js index d41a6343c..7b9618654 100755 --- a/backend/lib/constants.js +++ b/backend/lib/constants.js @@ -2,4 +2,8 @@ module.exports = { APPSEC_CONFIG_FILE_NAME: 'local_policy.yaml', APPSEC_EXT_DIR: '/ext/appsec', APPSEC_LOG_DIR: '/ext/appsec-logs', + USE_NOTIFY_POLICY: true, + PORTS: [7777, 7778], + HOSTURL: 'http://127.0.0.1', + POLICY_PATH: '/etc/cp/conf/local_policy.yaml', }; \ No newline at end of file diff --git a/frontend/js/app/openappsec-log/list-all/item.ejs b/frontend/js/app/openappsec-log/list-all/item.ejs index ecc0214cd..087ea989c 100644 --- a/frontend/js/app/openappsec-log/list-all/item.ejs +++ b/frontend/js/app/openappsec-log/list-all/item.ejs @@ -1,29 +1,6 @@ - - <%- formatDbDate(eventTime, 'D-M-YYYY, H:mm') %> - - -
    - <% var sevirityClass = 'bg-success'; - switch (eventSeverity) { - case 'Critical': - sevirityClass = 'bg-danger'; - break; - case 'Warning': - sevirityClass = 'bg-warning'; - break; - case 'Info': - sevirityClass = 'bg-success'; - //sevirityClass = 'bg-info'; - break; - case 'Debug': - sevirityClass = 'bg-success'; - break; - } - %> - <%- eventSeverity %> -
    - +<%- formatDbDate(eventTime, 'D-M-YY, H:mm') %> +<%- eventSeverity %> <%- assetName %> <%- securityAction %> <%- waapIncidentType %> @@ -34,10 +11,10 @@ <%- httpMethod %> <%- httpResponseCode %> <%- httpUriPath %> -<%- protectionName %> +<%- eventTopic %> <%- matchedLocation %> <%- matchedParameter %> <%- matchedSample %> open - + \ No newline at end of file diff --git a/frontend/js/app/openappsec-log/list-all/main.ejs b/frontend/js/app/openappsec-log/list-all/main.ejs index 184cddd4b..07ad186cd 100644 --- a/frontend/js/app/openappsec-log/list-all/main.ejs +++ b/frontend/js/app/openappsec-log/list-all/main.ejs @@ -11,7 +11,7 @@ HTTP Method HTTP Response Code HTTP URI Path - Protection Name + Event Topic Matched Location Matched Parameter Matched Sample diff --git a/frontend/js/app/openappsec-log/list-all/main.js b/frontend/js/app/openappsec-log/list-all/main.js index 777d2db76..55b8bcdef 100644 --- a/frontend/js/app/openappsec-log/list-all/main.js +++ b/frontend/js/app/openappsec-log/list-all/main.js @@ -8,7 +8,6 @@ let TableBody = Mn.CollectionView.extend({ initialize: function (options) { this.options = new Backbone.Model(options); - console.log("options: ", options); // this.page = options.page; // this.perPage = options.perPage; this.updatePage(); @@ -20,22 +19,12 @@ let TableBody = Mn.CollectionView.extend({ let page = this.page || 1; let models; if (this.perPage && this.page) { - console.log('updatePage2'); models = this.collection.models.slice((page - 1) * perPage, page * perPage); } else { - console.log('updatePage3'); - models = this.collection.models; } this.collection.reset(models); } - - // updatePage: function () { - // let perPage = this.perPage || this.collection.length; - // let page = this.page || 1; - // let models = this.collection.models.slice((page - 1) * perPage, page * perPage); - // this.collection.reset(models); - // } }); module.exports = Mn.View.extend({ diff --git a/frontend/js/app/openappsec-log/list-important/item.ejs b/frontend/js/app/openappsec-log/list-important/item.ejs index 5537babb6..a6f8b3a14 100644 --- a/frontend/js/app/openappsec-log/list-important/item.ejs +++ b/frontend/js/app/openappsec-log/list-important/item.ejs @@ -1,29 +1,6 @@ - - <%- formatDbDate(eventTime, 'D-M-YY, H:mm') %> - - -
    - <% var sevirityClass = 'bg-success'; - switch (eventSeverity) { - case 'Critical': - sevirityClass = 'bg-danger'; - break; - case 'Warning': - sevirityClass = 'bg-warning'; - break; - case 'Info': - sevirityClass = 'bg-success'; - //sevirityClass = 'bg-info'; - break; - case 'Debug': - sevirityClass = 'bg-success'; - break; - } - %> - <%- eventSeverity %> -
    - +<%- formatDbDate(eventTime, 'D-M-YY, H:mm') %> +<%- eventSeverity %> <%- assetName %> <%- securityAction %> <%- waapIncidentType %> @@ -34,7 +11,7 @@ <%- httpMethod %> <%- httpResponseCode %> <%- httpUriPath %> -<%- protectionName %> +<%- eventTopic %> <%- matchedLocation %> <%- matchedParameter %> <%- matchedSample %> diff --git a/frontend/js/app/openappsec-log/list-important/main.ejs b/frontend/js/app/openappsec-log/list-important/main.ejs index 184cddd4b..07ad186cd 100644 --- a/frontend/js/app/openappsec-log/list-important/main.ejs +++ b/frontend/js/app/openappsec-log/list-important/main.ejs @@ -11,7 +11,7 @@ HTTP Method HTTP Response Code HTTP URI Path - Protection Name + Event Topic Matched Location Matched Parameter Matched Sample diff --git a/frontend/js/app/openappsec-log/list-important/main.js b/frontend/js/app/openappsec-log/list-important/main.js index 0c90551ba..a7ef3281c 100644 --- a/frontend/js/app/openappsec-log/list-important/main.js +++ b/frontend/js/app/openappsec-log/list-important/main.js @@ -15,7 +15,6 @@ let TableBody = Mn.CollectionView.extend({ }, updatePage: function () { - console.log('updatePage'); let models = this.collection.models.slice((this.page - 1) * this.perPage, this.page * this.perPage); this.collection.reset(models); } diff --git a/frontend/js/app/openappsec-log/list-notifications/item.ejs b/frontend/js/app/openappsec-log/list-notifications/item.ejs index e901a23e7..86457f263 100644 --- a/frontend/js/app/openappsec-log/list-notifications/item.ejs +++ b/frontend/js/app/openappsec-log/list-notifications/item.ejs @@ -1,27 +1,5 @@ - <%- formatDbDate(eventTime, 'D-M-YY, H:mm') %> - -
    - <% var sevirityClass = 'bg-success'; - switch (eventSeverity) { - case 'Critical': - sevirityClass = 'bg-danger'; - break; - case 'Warning': - sevirityClass = 'bg-warning'; - break; - case 'Info': - sevirityClass = 'bg-success'; - //sevirityClass = 'bg-info'; - break; - case 'Debug': - sevirityClass = 'bg-success'; - break; - } - %> - <%- eventSeverity %> -
    - +<%- eventSeverity %> <%- eventPriority %> <%- eventTopic %> <%- eventName %> diff --git a/frontend/js/app/openappsec-log/list-notifications/main.js b/frontend/js/app/openappsec-log/list-notifications/main.js index 0c90551ba..a7ef3281c 100644 --- a/frontend/js/app/openappsec-log/list-notifications/main.js +++ b/frontend/js/app/openappsec-log/list-notifications/main.js @@ -15,7 +15,6 @@ let TableBody = Mn.CollectionView.extend({ }, updatePage: function () { - console.log('updatePage'); let models = this.collection.models.slice((this.page - 1) * this.perPage, this.page * this.perPage); this.collection.reset(models); } diff --git a/frontend/js/app/openappsec-log/main.js b/frontend/js/app/openappsec-log/main.js index 8f2ecad6b..cb9928772 100755 --- a/frontend/js/app/openappsec-log/main.js +++ b/frontend/js/app/openappsec-log/main.js @@ -182,24 +182,6 @@ module.exports = Mn.View.extend({ onTabClick: function(event) { event.preventDefault(); const selectedTab = event.target.id; - - console.log("selectedTab: ", selectedTab); - - // let ListView; - // switch (selectedTab) { - // case 'tab1': - // ListView = ListViewTab1; - // break; - // case 'tab2': - // ListView = ListViewTab2; - // break; - // case 'tab3': - // ListView = ListViewTab3; - // break; - // default: - // ListView = ListViewTab1; - // } - let view = this; let query = this.ui.query.val() || ''; From c0171fe78bd88f612fe1796baa102cced52feddb Mon Sep 17 00:00:00 2001 From: Rami Winestock Date: Thu, 14 Dec 2023 23:59:54 +0200 Subject: [PATCH 11/75] Update log table, login screen UI and package versions --- backend/internal/openappsec-log.js | 12 +++++ backend/package.json | 2 +- frontend/html/login.ejs | 4 +- .../js/app/openappsec-log/list-all/item.ejs | 9 ++-- .../js/app/openappsec-log/list-all/main.ejs | 8 ++-- .../openappsec-log/list-important/item.ejs | 7 +-- .../openappsec-log/list-important/main.ejs | 8 ++-- .../list-notifications/item.ejs | 8 ++-- .../list-notifications/main.ejs | 2 +- frontend/js/app/openappsec-log/main.js | 44 +++++-------------- frontend/js/login/ui/login.ejs | 7 ++- frontend/js/login/ui/login.js | 4 +- frontend/package.json | 2 +- 13 files changed, 54 insertions(+), 63 deletions(-) diff --git a/backend/internal/openappsec-log.js b/backend/internal/openappsec-log.js index dfdf19e5a..6a9486dbc 100755 --- a/backend/internal/openappsec-log.js +++ b/backend/internal/openappsec-log.js @@ -58,6 +58,18 @@ const internalOpenappsecLog = { eventSeverity: json.eventSeverity, eventLevel: json.eventLevel, eventTime: json.eventTime, + assetName: json.eventSource.assetName, + securityAction: json.eventData.securityAction, + waapIncidentType: json.eventData.waapIncidentType, + httpSourceId: json.eventData.httpSourceId, + sourceIP: json.eventData.sourceIP, + httpHostName: json.eventData.httpHostName, + httpMethod: json.eventData.httpMethod, + httpUriPath: json.eventData.httpUriPath, + eventTopic: json.eventSource.eventTopic, + matchedLocation: json.eventData.matchedLocation, + matchedParameter: json.eventData.matchedParameter, + matchedSample: json.eventData.matchedSample, eventName: json.eventName }; dataLines.push(wrappedObject); diff --git a/backend/package.json b/backend/package.json index 3227ec93c..cb0dee9ee 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "nginx-proxy-manager", - "version": "0.0.0", + "version": "1.0.1", "description": "A beautiful interface for creating Nginx endpoints", "main": "js/index.js", "dependencies": { diff --git a/frontend/html/login.ejs b/frontend/html/login.ejs index bc4b9a27f..d24a76e35 100644 --- a/frontend/html/login.ejs +++ b/frontend/html/login.ejs @@ -1,9 +1,9 @@ <% var title = 'Login – Nginx Proxy Manager' %> <%- include partials/header.ejs %> -
    +
    - + <%- include partials/footer.ejs %> diff --git a/frontend/js/app/openappsec-log/list-all/item.ejs b/frontend/js/app/openappsec-log/list-all/item.ejs index 087ea989c..0d24ab1bc 100644 --- a/frontend/js/app/openappsec-log/list-all/item.ejs +++ b/frontend/js/app/openappsec-log/list-all/item.ejs @@ -1,5 +1,5 @@ - -<%- formatDbDate(eventTime, 'D-M-YY, H:mm') %> +open +<%- formatDbDate(eventTime, 'MMM DD YYYY, H:mm') %> <%- eventSeverity %> <%- assetName %> <%- securityAction %> @@ -14,7 +14,4 @@ <%- eventTopic %> <%- matchedLocation %> <%- matchedParameter %> -<%- matchedSample %> - - open - \ No newline at end of file +<%- matchedSample %> \ No newline at end of file diff --git a/frontend/js/app/openappsec-log/list-all/main.ejs b/frontend/js/app/openappsec-log/list-all/main.ejs index 07ad186cd..e2fd8fa99 100644 --- a/frontend/js/app/openappsec-log/list-all/main.ejs +++ b/frontend/js/app/openappsec-log/list-all/main.ejs @@ -1,21 +1,21 @@ +   Time Event Severity Asset Name Security Action - AppSec Incident Type + AppSec Incident Type Source Identifier Source IP Proxy IP HTTP Host HTTP Method - HTTP Response Code - HTTP URI Path + HTTP Response Code + HTTP URI Path Event Topic Matched Location Matched Parameter Matched Sample -   diff --git a/frontend/js/app/openappsec-log/list-important/item.ejs b/frontend/js/app/openappsec-log/list-important/item.ejs index a6f8b3a14..d038f03d9 100644 --- a/frontend/js/app/openappsec-log/list-important/item.ejs +++ b/frontend/js/app/openappsec-log/list-important/item.ejs @@ -1,5 +1,5 @@ - -<%- formatDbDate(eventTime, 'D-M-YY, H:mm') %> +open +<%- formatDbDate(eventTime, 'MMM DD YYYY, H:mm') %> <%- eventSeverity %> <%- assetName %> <%- securityAction %> @@ -15,6 +15,3 @@ <%- matchedLocation %> <%- matchedParameter %> <%- matchedSample %> - - open - diff --git a/frontend/js/app/openappsec-log/list-important/main.ejs b/frontend/js/app/openappsec-log/list-important/main.ejs index 07ad186cd..e2fd8fa99 100644 --- a/frontend/js/app/openappsec-log/list-important/main.ejs +++ b/frontend/js/app/openappsec-log/list-important/main.ejs @@ -1,21 +1,21 @@ +   Time Event Severity Asset Name Security Action - AppSec Incident Type + AppSec Incident Type Source Identifier Source IP Proxy IP HTTP Host HTTP Method - HTTP Response Code - HTTP URI Path + HTTP Response Code + HTTP URI Path Event Topic Matched Location Matched Parameter Matched Sample -   diff --git a/frontend/js/app/openappsec-log/list-notifications/item.ejs b/frontend/js/app/openappsec-log/list-notifications/item.ejs index 86457f263..2c64efd41 100644 --- a/frontend/js/app/openappsec-log/list-notifications/item.ejs +++ b/frontend/js/app/openappsec-log/list-notifications/item.ejs @@ -1,10 +1,8 @@ -<%- formatDbDate(eventTime, 'D-M-YY, H:mm') %> +open +<%- formatDbDate(eventTime, 'MMM DD YYYY, H:mm') %> <%- eventSeverity %> <%- eventPriority %> <%- eventTopic %> <%- eventName %> <%- suggestedRemediation %> -<%- assetName %> - - open - +<%- assetName %> \ No newline at end of file diff --git a/frontend/js/app/openappsec-log/list-notifications/main.ejs b/frontend/js/app/openappsec-log/list-notifications/main.ejs index 9092a11ae..30b06c70c 100644 --- a/frontend/js/app/openappsec-log/list-notifications/main.ejs +++ b/frontend/js/app/openappsec-log/list-notifications/main.ejs @@ -1,4 +1,5 @@ +   Time Event Severity Event Priority @@ -6,7 +7,6 @@ Event Name Suggested Remediation if Applicable Asset Name -   diff --git a/frontend/js/app/openappsec-log/main.js b/frontend/js/app/openappsec-log/main.js index cb9928772..2afd08b1f 100755 --- a/frontend/js/app/openappsec-log/main.js +++ b/frontend/js/app/openappsec-log/main.js @@ -78,9 +78,9 @@ module.exports = Mn.View.extend({ }; this.tabPaginationStates = { - tab1: { page: 1, perPage: this.options.perPage }, - tab2: { page: 1, perPage: this.options.perPage }, - tab3: { page: 1, perPage: this.options.perPage } + tab1: { page: 1, perPage: this.options.perPage, totalDataLines: tab1Data.length }, + tab2: { page: 1, perPage: this.options.perPage, totalDataLines: tab2Data.length }, + tab3: { page: 1, perPage: this.options.perPage, totalDataLines: tab3Data.length } }; // Define an object mapping for the ListViews @@ -99,36 +99,16 @@ module.exports = Mn.View.extend({ // Show the ListView for the current tab this.showChildView('list_region', new CurrentListView({ collection: this.tabCollections[currentTab], - page: 1, - perPage: this.options.perPage - // page: this.tabPaginationStates[currentTab].page, - // perPage: this.tabPaginationStates[currentTab].perPage + // page: 1, + // perPage: this.options.perPage + page: this.tabPaginationStates[currentTab].page, + perPage: this.tabPaginationStates[currentTab].perPage })); - // const totalDataLines = response.length; - // this.showChildView('list_region', new ListView({ - // collection: this.tabCollections.tab1, - // page: this.tabPaginationStates.tab1.page, - // perPage: this.tabPaginationStates.tab1.perPage - // })); - - // this.showChildView('pagination_region', new PaginationView({ - // totalDataLines: this.tabCollectionLengths.tab1, - // totalPages: Math.ceil(this.tabCollectionLengths.tab1 / this.options.perPage), - // currentPage: this.tabPaginationStates.tab1.page - // })); - - // const totalDataLines = response.length; - // this.showChildView('list_region', new ListView({ - // collection: new OpenappsecLogModel.Collection(response), - // page: this.options.page, - // perPage: this.options.perPage - // })); - - // this.showChildView('pagination_region', new PaginationView({ - // totalDataLines: totalDataLines, - // totalPages: Math.ceil(totalDataLines / this.options.perPage), - // currentPage: this.options.page + // this.showChildView('pagination_region', new PaginationView({ + // totalDataLines: this.tabPaginationStates[currentTab].totalDataLines, + // totalPages: Math.ceil(this.tabPaginationStates[currentTab].totalDataLines / this.options.perPage), + // currentPage: this.tabPaginationStates[currentTab].page // })); }, @@ -184,7 +164,7 @@ module.exports = Mn.View.extend({ const selectedTab = event.target.id; let view = this; let query = this.ui.query.val() || ''; - + view.ui.dimmer.addClass('active'); view.fetch(['user'], query) .then(response => { if (!view.isDestroyed() && response) { diff --git a/frontend/js/login/ui/login.ejs b/frontend/js/login/ui/login.ejs index 693bc050c..925bc1b16 100644 --- a/frontend/js/login/ui/login.ejs +++ b/frontend/js/login/ui/login.ejs @@ -6,11 +6,16 @@
    -
    +
    Logo
    <%- i18n('main', 'version', {version: getVersion()}) %>
    +
    + Secured by: + + open-appsec +
    diff --git a/frontend/js/login/ui/login.js b/frontend/js/login/ui/login.js index 757eb4e31..dc90b642d 100644 --- a/frontend/js/login/ui/login.js +++ b/frontend/js/login/ui/login.js @@ -36,7 +36,9 @@ module.exports = Mn.View.extend({ templateContext: { i18n: i18n, getVersion: function () { - return $('#login').data('version'); + const version = require('../../../package.json').version; + return version; + // return $('#login').data('version'); } } }); diff --git a/frontend/package.json b/frontend/package.json index 4965d0dfb..6c3ea0d00 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "nginx-proxy-manager", - "version": "0.0.0", + "version": "1.0.1", "description": "A beautiful interface for creating Nginx endpoints", "main": "js/index.js", "devDependencies": { From f90f5edcd19814422e8dc86316d8661377bc926b Mon Sep 17 00:00:00 2001 From: Rami Winestock Date: Fri, 15 Dec 2023 06:43:35 +0200 Subject: [PATCH 12/75] login screen package version fix --- frontend/js/login/ui/login.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/js/login/ui/login.js b/frontend/js/login/ui/login.js index dc90b642d..39fca4233 100644 --- a/frontend/js/login/ui/login.js +++ b/frontend/js/login/ui/login.js @@ -36,9 +36,8 @@ module.exports = Mn.View.extend({ templateContext: { i18n: i18n, getVersion: function () { - const version = require('../../../package.json').version; + const version = $('#login').data('version'); return version; - // return $('#login').data('version'); } } }); From 3ae059ef424a9086afab722a1ad52cee34b4c4d4 Mon Sep 17 00:00:00 2001 From: Rami Winestock Date: Fri, 15 Dec 2023 17:57:31 +0200 Subject: [PATCH 13/75] log fields and logo fixes --- .../js/app/openappsec-log/list-all/item.ejs | 30 ++++++------ .../js/app/openappsec-log/list-all/item.js | 8 ++++ .../openappsec-log/list-important/item.ejs | 30 ++++++------ .../app/openappsec-log/list-important/item.js | 7 +++ .../list-notifications/item.ejs | 12 ++--- .../openappsec-log/list-notifications/item.js | 7 +++ frontend/js/login/ui/login.ejs | 22 ++++++--- frontend/js/models/openappsec-log.js | 46 +++++++++---------- 8 files changed, 97 insertions(+), 65 deletions(-) diff --git a/frontend/js/app/openappsec-log/list-all/item.ejs b/frontend/js/app/openappsec-log/list-all/item.ejs index 0d24ab1bc..c4c096852 100644 --- a/frontend/js/app/openappsec-log/list-all/item.ejs +++ b/frontend/js/app/openappsec-log/list-all/item.ejs @@ -1,17 +1,17 @@ open <%- formatDbDate(eventTime, 'MMM DD YYYY, H:mm') %> -<%- eventSeverity %> -<%- assetName %> -<%- securityAction %> -<%- waapIncidentType %> -<%- httpSourceId %> -<%- sourceIp %> -<%- proxyIp %> -<%- httpHostName %> -<%- httpMethod %> -<%- httpResponseCode %> -<%- httpUriPath %> -<%- eventTopic %> -<%- matchedLocation %> -<%- matchedParameter %> -<%- matchedSample %> \ No newline at end of file +<%= createSpecificTableCell(eventSeverity) %> +<%= createSpecificTableCell(assetName) %> +<%= createSpecificTableCell(securityAction) %> +<%= createSpecificTableCell(waapIncidentType) %> +<%= createSpecificTableCell(httpSourceId) %> +<%= createSpecificTableCell(sourceIP) %> +<%= createSpecificTableCell(proxyIp) %> +<%= createSpecificTableCell(httpHostName) %> +<%= createSpecificTableCell(httpMethod) %> +<%= createSpecificTableCell(httpResponseCode) %> +<%= createSpecificTableCell(httpUriPath) %> +<%= createSpecificTableCell(eventTopic) %> +<%= createSpecificTableCell(matchedLocation) %> +<%= createSpecificTableCell(matchedParameter) %> +<%= createSpecificTableCell(matchedSample) %> diff --git a/frontend/js/app/openappsec-log/list-all/item.js b/frontend/js/app/openappsec-log/list-all/item.js index 009e3a3e6..a65006f02 100644 --- a/frontend/js/app/openappsec-log/list-all/item.js +++ b/frontend/js/app/openappsec-log/list-all/item.js @@ -27,6 +27,14 @@ module.exports = Mn.View.extend({ } return '#' + (this.object_id || '?'); + }, + createSpecificTableCell: function(value) { + if (value && value.trim() !== '') { + return `${value}`; + } else { + return `-`; + } } + } }); diff --git a/frontend/js/app/openappsec-log/list-important/item.ejs b/frontend/js/app/openappsec-log/list-important/item.ejs index d038f03d9..266b9597e 100644 --- a/frontend/js/app/openappsec-log/list-important/item.ejs +++ b/frontend/js/app/openappsec-log/list-important/item.ejs @@ -1,17 +1,17 @@ open <%- formatDbDate(eventTime, 'MMM DD YYYY, H:mm') %> -<%- eventSeverity %> -<%- assetName %> -<%- securityAction %> -<%- waapIncidentType %> -<%- httpSourceId %> -<%- sourceIp %> -<%- proxyIp %> -<%- httpHostName %> -<%- httpMethod %> -<%- httpResponseCode %> -<%- httpUriPath %> -<%- eventTopic %> -<%- matchedLocation %> -<%- matchedParameter %> -<%- matchedSample %> +<%= createSpecificTableCell(eventSeverity) %> +<%= createSpecificTableCell(assetName) %> +<%= createSpecificTableCell(securityAction) %> +<%= createSpecificTableCell(waapIncidentType) %> +<%= createSpecificTableCell(httpSourceId) %> +<%= createSpecificTableCell(sourceIP) %> +<%= createSpecificTableCell(proxyIp) %> +<%= createSpecificTableCell(httpHostName) %> +<%= createSpecificTableCell(httpMethod) %> +<%= createSpecificTableCell(httpResponseCode) %> +<%= createSpecificTableCell(httpUriPath) %> +<%= createSpecificTableCell(eventTopic) %> +<%= createSpecificTableCell(matchedLocation) %> +<%= createSpecificTableCell(matchedParameter) %> +<%= createSpecificTableCell(matchedSample) %> \ No newline at end of file diff --git a/frontend/js/app/openappsec-log/list-important/item.js b/frontend/js/app/openappsec-log/list-important/item.js index 009e3a3e6..22a358e5d 100644 --- a/frontend/js/app/openappsec-log/list-important/item.js +++ b/frontend/js/app/openappsec-log/list-important/item.js @@ -27,6 +27,13 @@ module.exports = Mn.View.extend({ } return '#' + (this.object_id || '?'); + }, + createSpecificTableCell: function(value) { + if (value && value.trim() !== '') { + return `${value}`; + } else { + return `-`; + } } } }); diff --git a/frontend/js/app/openappsec-log/list-notifications/item.ejs b/frontend/js/app/openappsec-log/list-notifications/item.ejs index 2c64efd41..f97a9c49b 100644 --- a/frontend/js/app/openappsec-log/list-notifications/item.ejs +++ b/frontend/js/app/openappsec-log/list-notifications/item.ejs @@ -1,8 +1,8 @@ open <%- formatDbDate(eventTime, 'MMM DD YYYY, H:mm') %> -<%- eventSeverity %> -<%- eventPriority %> -<%- eventTopic %> -<%- eventName %> -<%- suggestedRemediation %> -<%- assetName %> \ No newline at end of file +<%= createSpecificTableCell(eventSeverity) %> +<%= createSpecificTableCell(eventPriority) %> +<%= createSpecificTableCell(eventTopic) %> +<%= createSpecificTableCell(eventName) %> +<%= createSpecificTableCell(suggestedRemediation) %> +<%= createSpecificTableCell(assetName) %> \ No newline at end of file diff --git a/frontend/js/app/openappsec-log/list-notifications/item.js b/frontend/js/app/openappsec-log/list-notifications/item.js index 009e3a3e6..22a358e5d 100644 --- a/frontend/js/app/openappsec-log/list-notifications/item.js +++ b/frontend/js/app/openappsec-log/list-notifications/item.js @@ -27,6 +27,13 @@ module.exports = Mn.View.extend({ } return '#' + (this.object_id || '?'); + }, + createSpecificTableCell: function(value) { + if (value && value.trim() !== '') { + return `${value}`; + } else { + return `-`; + } } } }); diff --git a/frontend/js/login/ui/login.ejs b/frontend/js/login/ui/login.ejs index 925bc1b16..3f603acf2 100644 --- a/frontend/js/login/ui/login.ejs +++ b/frontend/js/login/ui/login.ejs @@ -7,14 +7,24 @@
    +
    Logo -
    - <%- i18n('main', 'version', {version: getVersion()}) %> +
    + <%- i18n('main', 'version' , {version: getVersion()}) %>
    -
    - Secured by: - - open-appsec + +
    +
    + Secured by: +
    +
    + +
    +
    + open-appsec +
    +
    +
    diff --git a/frontend/js/models/openappsec-log.js b/frontend/js/models/openappsec-log.js index 5f0575389..9ee4c05c2 100755 --- a/frontend/js/models/openappsec-log.js +++ b/frontend/js/models/openappsec-log.js @@ -5,30 +5,30 @@ const model = Backbone.Model.extend({ defaults: function () { return { - name: '-', - eventSeverity: '-', - assetName: '-', - securityAction: '-', - waapIncidentType: '-', - httpSourceId: '-', - sourceIp: '-', - // 'Proxy-IP': '-', - proxyIp: '-', - httpHostName: '-', - httpMethod: '-', - // 'HTTP-Response-Code': '-', - httpResponseCode: '-', - httpUriPath: '-', - // 'Protection-Name': '-', - protectionName: '-', - matchedLocation: '-', - matchedParameter: '-', - matchedSample: '-', - eventPriority: '-', - eventTopic: '-', - eventName: '-', + name: '', + eventSeverity: '', + assetName: '', + securityAction: '', + waapIncidentType: '', + httpSourceId: '', + sourceIP: '', + // 'Proxy-IP': '', + proxyIp: '', + httpHostName: '', + httpMethod: '', + // 'HTTP-Response-Code': '', + httpResponseCode: '', + httpUriPath: '', + // 'Protection-Name': '', + protectionName: '', + matchedLocation: '', + matchedParameter: '', + matchedSample: '', + eventPriority: '', + eventTopic: '', + eventName: '', // Suggested Remediation if Applicable - suggestedRemediation: '-' + suggestedRemediation: '' }; } }); From 1c493ea921022d326a505a044031acc6d992a931 Mon Sep 17 00:00:00 2001 From: clutat <104208687+clutat@users.noreply.github.com> Date: Mon, 18 Dec 2023 12:07:33 +0100 Subject: [PATCH 14/75] Update Dockerfile --- docker/Dockerfile | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 82ec9355a..df4d1f611 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -42,9 +42,20 @@ WORKDIR /app RUN yarn install \ && yarn cache clean +# add open-appsec attachment +RUN mkdir -p /usr/lib/nginx +RUN mkdir -p /usr/lib/nginx/modules +COPY docker/lib/libngx_module.so /usr/lib/nginx/modules/libngx_module.so +COPY docker/lib/libosrc_nginx_attachment_util.so /usr/lib/libosrc_nginx_attachment_util.so +COPY docker/lib/libosrc_compression_utils.so /usr/lib/libosrc_compression_utils.so +COPY docker/lib/libosrc_shmem_ipc.so /usr/lib/libosrc_shmem_ipc.so + # add late to limit cache-busting by modifications COPY docker/rootfs / +# patch nginx.conf for open-appsec attachment +RUN sed -i -e '/include \/etc\/nginx\/modules\/\*\.conf/a\load_module /usr/lib/nginx/modules/libngx_module.so;' -e '/http {/a\\tcp_worker_processes auto;' /etc/nginx/nginx.conf + # Remove frontend service not required for prod, dev nginx config as well RUN rm -rf /etc/s6-overlay/s6-rc.d/user/contents.d/frontend /etc/nginx/conf.d/dev.conf \ && chmod 644 /etc/logrotate.d/nginx-proxy-manager \ From 84ee15f0b3dca5bd447690f91c2640e57edfbc06 Mon Sep 17 00:00:00 2001 From: clutat <104208687+clutat@users.noreply.github.com> Date: Mon, 18 Dec 2023 12:47:28 +0100 Subject: [PATCH 15/75] Update README.md --- README.md | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/README.md b/README.md index 8b1378917..33588f0a4 100644 --- a/README.md +++ b/README.md @@ -1 +1,102 @@ +# About +Nginx Proxy Manager is a popular open-source project that simplifies the management of NGINX reverse proxy configurations, offering a user-friendly web-based interface for easy setup and maintenance. It was created by “jc21”. +This project is particularly useful for individuals and organizations looking to streamline the deployment of web applications and services by efficiently managing multiple domains and subdomains through a centralized interface. +With NGINX Proxy Manager, users can effortlessly create and manage SSL certificates, enabling secure HTTPS connections for their applications, while also providing advanced features such as Let's Encrypt integration for automated certificate renewal. +NGINX Proxy Manager (NPM) is based on NGINX and provided as a container image that can be easily deployed in containerized environments like Docker (typically using Docker Compose) or others. +NPM itself does not include any WAF solution for effective Threat Prevention against modern attacks or Zero-day attacks. + +Website and Docs: nginxproxymanager.com Github: www.github.com/NginxProxyManager + +### Integration of open-appsec WAF with NGINX Proxy Manager: +This new integration not only closes the security gap caused by the soon-end-of-life ModSecurity but provides a modern, strong protection alternative in the form of open-appsec, a preemptive, machine-learning-based, fully automatic WAF that does not rely on signatures at all. +While developing this integration we focused on maximum simplicity to maintain the low entry barrier of the NGINX proxy manager (NPM) project. +The actual deployment of NPM with open-appsec is performed using a slightly enhanced docker-compose file (see below) and configuring open-appsec can be done from an enhanced NPM Web UI interface to which the relevant configuration options for the open-appsec WAF, as well as an option to view the open-appsec logs, were added. + +# Deployment: +Before you start, make sure to have a Linux environment with Docker and Docker Compose available. +To deploy NGINX Proxy Manager with open-appsec integration follow the steps below: +1. Within the directory which you want to use for the deployment: +Create a folder appsec-localconfig which will hold the appsec declarative configuration file (this will be managed by the enhanced NPM WebUI) +``` +mkdir ./appsec-localconfig +``` +2. Download the initial declarative configuration file for open-appsec into that folder: +``` +wget https://raw.githubusercontent.com/openappsec/open-appsec-npm/main/deployment/local_policy.yaml + -O ./appsec-localconfig/local_policy.yaml +``` +3. Create a docker-compose.yaml file with the content below, it can be downloaded as follows: +``` +wget https://raw.githubusercontent.com/openappsec/open-appsec-npm/main/deployment/docker-compose.yaml +docker-compose.yaml content: +version: '3.8' +# docker compose for npm open-appsec integration +services: + appsec-npm: + container_name: appsec-npm + image: 'ghcr.io/openappsec/open-appsec-npm:latest' + ipc: host + restart: unless-stopped + ports: + - '80:80' # Public HTTP Port + - '443:443' # Public HTTPS Port + - '81:81' # Admin Web Port + volumes: + - ./data:/data + - ./letsencrypt:/etc/letsencrypt + - ./appsec-logs:/ext/appsec-logs + - ./appsec-localconfig:/ext/appsec + + appsec-agent: + container_name: appsec-agent + image: 'ghcr.io/openappsec/agent:latest' + network_mode: service:appsec-npm + ipc: host + restart: unless-stopped + environment: + # adjust with your own email below + - user_email=user@email.com + - nginxproxymanager=true + - autoPolicyLoad=true + volumes: + - ./appsec-config:/etc/cp/conf + - ./appsec-data:/etc/cp/data + - ./appsec-logs:/var/log/nano_agent + - ./appsec-localconfig:/ext/appsec + command: /cp-nano-agent --standalone +``` +4. Run docker-compose up to start the deployment of all relevant containers: +``` +docker-compose up +``` +5. Check if the appsec-npm and the appsec-agent containers are up and running: +``` +docker ps +``` +Congratulations, now you are all set and you can login with your web browser to the WebUI of NGINX Proxy Manager with open-appsec integration as follows: +http://[hostname or IP of your host]:81 + +# Configuration +To learn how to use NGINX Proxy Manager (NPM) see project documentation: https://nginx-proxy-manager.com (NPM usage and configuration will not be explained here). + +Once you created a new Proxy Host within NGINX Proxy Manager WebUI you can now easily enable and configure open-appsec protection (see also screenshot below): +1. Enable open-appsec by flipping the “open-appsec” switch to enabled. +2. Select the Enforcement Mode, it can be either “Prevent-Learn” or “Detect-Learn” +3. Select the minimum confidence level for open-appsec to prevent an attack (only relevant when in prevent mode), it can be either “Critical”, “High” or “Medium” confidence. +4. Click “Save” +![image](https://github.com/openappsec/open-appsec-npm/assets/126462046/2704d0dd-a4b6-44bd-adc0-3759c74bd702) + +# Compilation + +# Contributing +We welcome everyone that wishes to share their knowledge and expertise to enhance and expand the project. + +Please see the [Contributing Guidelines](https://github.com/openappsec/openappsec/blob/main/CONTRIBUTING.md). + +# License +open-appsec is open source and available under Apache 2.0 license. + +The basic ML model is open source and available under Apache 2.0 license. + +The advanced ML model is open source and available under Machine Learning Model license, available upon download in the tar file. From f2564e9eb531243c450f6f593c9a3ed424708525 Mon Sep 17 00:00:00 2001 From: clutat <104208687+clutat@users.noreply.github.com> Date: Mon, 18 Dec 2023 16:29:35 +0100 Subject: [PATCH 16/75] Update README.md --- README.md | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 102 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 33588f0a4..aa87359fa 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,35 @@ -# About +# open-appsec NPM Proxy Manager integration (beta) + +This is the repository for the beta release of the new integration of open-appsec WAF with NGINX Proxy Manager. +This will allow NGINX Proxy Manager (NPM) users to protect their web applications and web APIs exposed by NGINX Proxy Manager by easily activating and configuring open-appsec protection for each of the configured Proxy Host objects in NPM directly from the NPM Web UI and monitor security events. + +### NGINX Proxy Manager Nginx Proxy Manager is a popular open-source project that simplifies the management of NGINX reverse proxy configurations, offering a user-friendly web-based interface for easy setup and maintenance. It was created by “jc21”. This project is particularly useful for individuals and organizations looking to streamline the deployment of web applications and services by efficiently managing multiple domains and subdomains through a centralized interface. With NGINX Proxy Manager, users can effortlessly create and manage SSL certificates, enabling secure HTTPS connections for their applications, while also providing advanced features such as Let's Encrypt integration for automated certificate renewal. NGINX Proxy Manager (NPM) is based on NGINX and provided as a container image that can be easily deployed in containerized environments like Docker (typically using Docker Compose) or others. NPM itself does not include any WAF solution for effective Threat Prevention against modern attacks or Zero-day attacks. -Website and Docs: nginxproxymanager.com Github: www.github.com/NginxProxyManager +Website and Docs: https://nginxproxymanager.com +Github: https://github.com/NginxProxyManager + +### open-appsec WAF: + +open-appsec WAF provides automatic, preemptive threat prevention for reverse proxies like NGINX. It is machine learning based, which means it doesn’t require signatures (or updating them) at all. This enables it to provide state-of-the art threat prevention even for true zero-day attacks and to significantly reduce administrative effort while strongly reducing the amount of false-positives. +open-appsec is a perfect fit to provide advanced threat prevention to the services exposed by NGINX Proxy Manager. + +Website: https://www.openappsec.io +Github: https://github.com/openappsec +Docs: https://docs.openappsec.io + ### Integration of open-appsec WAF with NGINX Proxy Manager: This new integration not only closes the security gap caused by the soon-end-of-life ModSecurity but provides a modern, strong protection alternative in the form of open-appsec, a preemptive, machine-learning-based, fully automatic WAF that does not rely on signatures at all. While developing this integration we focused on maximum simplicity to maintain the low entry barrier of the NGINX proxy manager (NPM) project. The actual deployment of NPM with open-appsec is performed using a slightly enhanced docker-compose file (see below) and configuring open-appsec can be done from an enhanced NPM Web UI interface to which the relevant configuration options for the open-appsec WAF, as well as an option to view the open-appsec logs, were added. +Documentation: https://docs.openappsec.io/integrations/nginx-proxy-manager-integration + # Deployment: Before you start, make sure to have a Linux environment with Docker and Docker Compose available. To deploy NGINX Proxy Manager with open-appsec integration follow the steps below: @@ -20,7 +38,8 @@ Create a folder appsec-localconfig which will hold the appsec declarative config ``` mkdir ./appsec-localconfig ``` -2. Download the initial declarative configuration file for open-appsec into that folder: +2. Download the initial declarative configuration file for open-appsec into that folder. +This will be managed from the NPM WebUI. ``` wget https://raw.githubusercontent.com/openappsec/open-appsec-npm/main/deployment/local_policy.yaml -O ./appsec-localconfig/local_policy.yaml @@ -74,22 +93,91 @@ docker-compose up docker ps ``` Congratulations, now you are all set and you can login with your web browser to the WebUI of NGINX Proxy Manager with open-appsec integration as follows: +``` http://[hostname or IP of your host]:81 +``` # Configuration -To learn how to use NGINX Proxy Manager (NPM) see project documentation: https://nginx-proxy-manager.com (NPM usage and configuration will not be explained here). +To learn how to use NGINX Proxy Manager (NPM) please see project documentation, as NPM usage and configuration will not be explained here: +https://nginx-proxy-manager.com Once you created a new Proxy Host within NGINX Proxy Manager WebUI you can now easily enable and configure open-appsec protection (see also screenshot below): 1. Enable open-appsec by flipping the “open-appsec” switch to enabled. 2. Select the Enforcement Mode, it can be either “Prevent-Learn” or “Detect-Learn” 3. Select the minimum confidence level for open-appsec to prevent an attack (only relevant when in prevent mode), it can be either “Critical”, “High” or “Medium” confidence. 4. Click “Save” + ![image](https://github.com/openappsec/open-appsec-npm/assets/126462046/2704d0dd-a4b6-44bd-adc0-3759c74bd702) -# Compilation +If you want to check out the open-appsec Logs click on the new menu option “Security Log” which allows you to view the open-appsec specific logs directly from the NPM Web UI: + +[TBD ADD SCREENSHOT LOG VIEW] + +This was just a very basic overview to get you started, there's many more things you can configure as part of the open-appsec NGINX Proxy Manager integration. + +**You find the full documentation including FAQ here: +https://docs.openappsec.io/integrations/nginx-proxy-manager-integration/** + +# Compilation Instructions + +**Important: In order to deploy and use the open-appsec NGINX Proxy Manager integration you do not have to compile the code yourself.** +We offer a pre-compiled ready-to-use "open-appsec-npm" container (see above in the "Deployment" section). + +## Prerequisites +- Linux Machine +- Precompiled libraries for open-appsec Attachment for the specific version of NGINX Proxy Manager +You find the detailed compilation instructions in this repo: +https://github.com/openappsec/attachment +Make sure to compile the attachment using a container with the exact same OS version as used in the NPM container version you want to build the integration upon and provide also the NGINX version information from that container. +libngx_module.so libosrc_compression_utils.so libosrc_nginx_attachment_util.so libosrc_shmem_ipc.so + +## Preparations +-- +Clone this repository to your local Linux machine: +``` +git clone https://github.com/openappsec/open-appsec-npm.git +``` + +Change into the open-appsec-npm directory: +``` +cd open-appsec-npm/scripts/ci +``` + +Create a folder for the already compiled libraries for the open-appsec attachment: +``` +mkdir -r /docker/lib +``` + +Copy the following already compiled Attachment files (see "Prerequisites" above) into the docker/lib/ folder: +``` +libngx_module.so +libosrc_nginx_attachment_util.so +libosrc_compression_utils.so +libosrc_shmem_ipc.so +``` + +Compilation - Build the Frontend: +--- +``` +cd scripts/ci +bash frontend-build +``` + +Build the open-appsec-npm Container Image: +-- +``` +cd ../.. +docker buildx build --load -t open-appsec-npm -f docker/Dockerfile . +``` + +**Congratulations, you have successfully built your own open-appsec-npm container.** +You can deploy it now by specifying it within the docker-compose.yaml file. +You find the deployment instructions above in the "Deployment" section. +If you also want to build your own open-appsec Agent container you find the instructions in this repo: +https://github.com/openappsec/openappsec # Contributing -We welcome everyone that wishes to share their knowledge and expertise to enhance and expand the project. +We welcome everyone that wishes to share their knowledge and expertise to enhance and expand this project. Please see the [Contributing Guidelines](https://github.com/openappsec/openappsec/blob/main/CONTRIBUTING.md). @@ -100,3 +188,11 @@ The basic ML model is open source and available under Apache 2.0 license. The advanced ML model is open source and available under Machine Learning Model license, available upon download in the tar file. +# Final notes + +We hope this integration will be useful for you and provide you easy-to-configure yet highly effective protection based on open-appsec for your web services or web APIs against known and especially unknown, zero day attacks! + +If you have any questions, feedback or need assistance with some issue you can +- contact us at info@openappsec.io +- contact us using the chat on our project website https://www.openappsec.io +- open an issue in the GitHub project From 2a3348ea3d68c1a18312558058c5061cb92f7358 Mon Sep 17 00:00:00 2001 From: clutat <104208687+clutat@users.noreply.github.com> Date: Mon, 18 Dec 2023 16:31:21 +0100 Subject: [PATCH 17/75] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index aa87359fa..4f3f854c8 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,6 @@ Make sure to compile the attachment using a container with the exact same OS ver libngx_module.so libosrc_compression_utils.so libosrc_nginx_attachment_util.so libosrc_shmem_ipc.so ## Preparations --- Clone this repository to your local Linux machine: ``` git clone https://github.com/openappsec/open-appsec-npm.git From 256a7501f09cacd7f87967f1d44b158b94d00014 Mon Sep 17 00:00:00 2001 From: clutat <104208687+clutat@users.noreply.github.com> Date: Mon, 18 Dec 2023 16:34:17 +0100 Subject: [PATCH 18/75] Update docker-compose.yaml - work on initial version --- deployment/docker-compose.yaml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/deployment/docker-compose.yaml b/deployment/docker-compose.yaml index a7e7f8e49..b610742ff 100644 --- a/deployment/docker-compose.yaml +++ b/deployment/docker-compose.yaml @@ -1,5 +1,6 @@ version: '3.8' # docker compose for npm open-appsec integration + services: appsec-npm: container_name: appsec-npm @@ -33,11 +34,3 @@ services: - ./appsec-logs:/var/log/nano_agent - ./appsec-localconfig:/ext/appsec command: /cp-nano-agent --standalone - - nginx: - container_name: nginx-backend - image: nginx:latest - ports: - - "8080:80" - volumes: - - ./nginx-deployment:/etc/nginx/conf.d From 86317919996a5b9b042f6041d00215051df21257 Mon Sep 17 00:00:00 2001 From: Rami Winestock Date: Mon, 18 Dec 2023 18:23:02 +0200 Subject: [PATCH 19/75] wide page with sticky table heads. updates local-policy default. data fields fixes. --- backend/internal/openappsec-log.js | 1 + .../local-policy-open-appsec-enabled-for-proxy-host.yaml | 5 +++-- frontend/js/app/openappsec-log/list-all/item.ejs | 3 +-- frontend/js/app/openappsec-log/list-all/main.ejs | 3 +-- frontend/js/app/openappsec-log/list-important/item.ejs | 3 +-- frontend/js/app/openappsec-log/list-important/main.ejs | 3 +-- frontend/js/app/openappsec-log/list-notifications/main.ejs | 2 +- frontend/js/app/openappsec-log/main.ejs | 2 +- frontend/js/app/ui/main.ejs | 2 +- frontend/js/models/openappsec-log.js | 2 +- 10 files changed, 12 insertions(+), 14 deletions(-) diff --git a/backend/internal/openappsec-log.js b/backend/internal/openappsec-log.js index 6a9486dbc..dfa770a57 100755 --- a/backend/internal/openappsec-log.js +++ b/backend/internal/openappsec-log.js @@ -63,6 +63,7 @@ const internalOpenappsecLog = { waapIncidentType: json.eventData.waapIncidentType, httpSourceId: json.eventData.httpSourceId, sourceIP: json.eventData.sourceIP, + proxyIP: json.eventData.proxyIP, httpHostName: json.eventData.httpHostName, httpMethod: json.eventData.httpMethod, httpUriPath: json.eventData.httpUriPath, diff --git a/backend/templates/local-policy-open-appsec-enabled-for-proxy-host.yaml b/backend/templates/local-policy-open-appsec-enabled-for-proxy-host.yaml index f4e1ef19f..6b560b3f8 100755 --- a/backend/templates/local-policy-open-appsec-enabled-for-proxy-host.yaml +++ b/backend/templates/local-policy-open-appsec-enabled-for-proxy-host.yaml @@ -80,6 +80,7 @@ log-triggers: enabled: true minimum-severity: high response-body: false + response-code: true appsec-logging: all-web-requests: false detect-events: true @@ -87,8 +88,8 @@ log-triggers: extended-logging: http-headers: false request-body: false - url-path: false - url-query: false + url-path: true + url-query: true log-destination: cloud: false stdout: diff --git a/frontend/js/app/openappsec-log/list-all/item.ejs b/frontend/js/app/openappsec-log/list-all/item.ejs index c4c096852..27f057d92 100644 --- a/frontend/js/app/openappsec-log/list-all/item.ejs +++ b/frontend/js/app/openappsec-log/list-all/item.ejs @@ -6,12 +6,11 @@ <%= createSpecificTableCell(waapIncidentType) %> <%= createSpecificTableCell(httpSourceId) %> <%= createSpecificTableCell(sourceIP) %> -<%= createSpecificTableCell(proxyIp) %> +<%= createSpecificTableCell(proxyIP) %> <%= createSpecificTableCell(httpHostName) %> <%= createSpecificTableCell(httpMethod) %> <%= createSpecificTableCell(httpResponseCode) %> <%= createSpecificTableCell(httpUriPath) %> -<%= createSpecificTableCell(eventTopic) %> <%= createSpecificTableCell(matchedLocation) %> <%= createSpecificTableCell(matchedParameter) %> <%= createSpecificTableCell(matchedSample) %> diff --git a/frontend/js/app/openappsec-log/list-all/main.ejs b/frontend/js/app/openappsec-log/list-all/main.ejs index e2fd8fa99..3357f286e 100644 --- a/frontend/js/app/openappsec-log/list-all/main.ejs +++ b/frontend/js/app/openappsec-log/list-all/main.ejs @@ -1,4 +1,4 @@ - +   Time Event Severity @@ -12,7 +12,6 @@ HTTP Method HTTP Response Code HTTP URI Path - Event Topic Matched Location Matched Parameter Matched Sample diff --git a/frontend/js/app/openappsec-log/list-important/item.ejs b/frontend/js/app/openappsec-log/list-important/item.ejs index 266b9597e..a311a62c0 100644 --- a/frontend/js/app/openappsec-log/list-important/item.ejs +++ b/frontend/js/app/openappsec-log/list-important/item.ejs @@ -6,12 +6,11 @@ <%= createSpecificTableCell(waapIncidentType) %> <%= createSpecificTableCell(httpSourceId) %> <%= createSpecificTableCell(sourceIP) %> -<%= createSpecificTableCell(proxyIp) %> +<%= createSpecificTableCell(proxyIP) %> <%= createSpecificTableCell(httpHostName) %> <%= createSpecificTableCell(httpMethod) %> <%= createSpecificTableCell(httpResponseCode) %> <%= createSpecificTableCell(httpUriPath) %> -<%= createSpecificTableCell(eventTopic) %> <%= createSpecificTableCell(matchedLocation) %> <%= createSpecificTableCell(matchedParameter) %> <%= createSpecificTableCell(matchedSample) %> \ No newline at end of file diff --git a/frontend/js/app/openappsec-log/list-important/main.ejs b/frontend/js/app/openappsec-log/list-important/main.ejs index e2fd8fa99..3357f286e 100644 --- a/frontend/js/app/openappsec-log/list-important/main.ejs +++ b/frontend/js/app/openappsec-log/list-important/main.ejs @@ -1,4 +1,4 @@ - +   Time Event Severity @@ -12,7 +12,6 @@ HTTP Method HTTP Response Code HTTP URI Path - Event Topic Matched Location Matched Parameter Matched Sample diff --git a/frontend/js/app/openappsec-log/list-notifications/main.ejs b/frontend/js/app/openappsec-log/list-notifications/main.ejs index 30b06c70c..73c69806e 100644 --- a/frontend/js/app/openappsec-log/list-notifications/main.ejs +++ b/frontend/js/app/openappsec-log/list-notifications/main.ejs @@ -1,4 +1,4 @@ - +   Time Event Severity diff --git a/frontend/js/app/openappsec-log/main.ejs b/frontend/js/app/openappsec-log/main.ejs index 4beee89c0..0b486f17f 100755 --- a/frontend/js/app/openappsec-log/main.ejs +++ b/frontend/js/app/openappsec-log/main.ejs @@ -30,7 +30,7 @@
    -
    +
    diff --git a/frontend/js/app/ui/main.ejs b/frontend/js/app/ui/main.ejs index b62c3acda..7a7cd046e 100644 --- a/frontend/js/app/ui/main.ejs +++ b/frontend/js/app/ui/main.ejs @@ -8,7 +8,7 @@
    -
    +
    diff --git a/frontend/js/models/openappsec-log.js b/frontend/js/models/openappsec-log.js index 9ee4c05c2..2388f92b4 100755 --- a/frontend/js/models/openappsec-log.js +++ b/frontend/js/models/openappsec-log.js @@ -13,7 +13,7 @@ const model = Backbone.Model.extend({ httpSourceId: '', sourceIP: '', // 'Proxy-IP': '', - proxyIp: '', + proxyIP: '', httpHostName: '', httpMethod: '', // 'HTTP-Response-Code': '', From 69f0ceb52939179d183d6a723f18d94aa28d2a1c Mon Sep 17 00:00:00 2001 From: Rami Winestock Date: Mon, 18 Dec 2023 22:00:27 +0200 Subject: [PATCH 20/75] Update API endpoint for applying policy --- backend/internal/nginx-openappsec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/internal/nginx-openappsec.js b/backend/internal/nginx-openappsec.js index 1520fbd22..74634100d 100755 --- a/backend/internal/nginx-openappsec.js +++ b/backend/internal/nginx-openappsec.js @@ -224,7 +224,7 @@ const internalNginxOpenappsec = { for (let port of ports) { try { - const command = `curl -s -o /dev/null -w "%{http_code}" ${constants.HOSTURL}:${port}/openappsec/apply-policy`; + const command = `curl -s -o /dev/null -w "%{http_code}" ${constants.HOSTURL}:${port}/openappsec/set-apply-policy`; console.log(`command: ${command}`); let { stdout } = await execPromise(command); if (stdout === '200') { From b105a2c3bf54373cdb64f9315c68d06ddc1cd4e1 Mon Sep 17 00:00:00 2001 From: Rami Winestock Date: Mon, 18 Dec 2023 22:50:41 +0200 Subject: [PATCH 21/75] Add Docker Compose file and local policy configuration --- deployment/docker-compose.yaml | 36 ++++++++++++++++++++ deployment/local_policy.yaml | 62 ++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 deployment/docker-compose.yaml create mode 100644 deployment/local_policy.yaml diff --git a/deployment/docker-compose.yaml b/deployment/docker-compose.yaml new file mode 100644 index 000000000..b610742ff --- /dev/null +++ b/deployment/docker-compose.yaml @@ -0,0 +1,36 @@ +version: '3.8' +# docker compose for npm open-appsec integration + +services: + appsec-npm: + container_name: appsec-npm + image: 'ghcr.io/openappsec/appsec-npm:latest' + ipc: host + restart: unless-stopped + ports: + - '80:80' # Public HTTP Port + - '443:443' # Public HTTPS Port + - '81:81' # Admin Web Port + volumes: + - ./data:/data + - ./letsencrypt:/etc/letsencrypt + - ./appsec-logs:/ext/appsec-logs + - ./appsec-localconfig:/ext/appsec + + appsec-agent: + container_name: appsec-agent + image: 'ghcr.io/openappsec/agent:latest' + network_mode: service:appsec-npm + ipc: host + restart: unless-stopped + environment: + # adjust with your own email below + - user_email=user@email.com + - nginxproxymanager=true + - autoPolicyLoad=true + volumes: + - ./appsec-config:/etc/cp/conf + - ./appsec-data:/etc/cp/data + - ./appsec-logs:/var/log/nano_agent + - ./appsec-localconfig:/ext/appsec + command: /cp-nano-agent --standalone diff --git a/deployment/local_policy.yaml b/deployment/local_policy.yaml new file mode 100644 index 000000000..8aaf15a86 --- /dev/null +++ b/deployment/local_policy.yaml @@ -0,0 +1,62 @@ +policies: + default: + triggers: + - appsec-default-log-trigger + mode: inactive + practices: + - webapp-default-practice + custom-response: appsec-default-web-user-response + specific-rules: [] + +practices: + - name: webapp-default-practice + web-attacks: + max-body-size-kb: 1000000 + max-header-size-bytes: 102400 + max-object-depth: 40 + max-url-size-bytes: 32768 + minimum-confidence: high + override-mode: inactive + protections: + csrf-protection: inactive + error-disclosure: inactive + non-valid-http-methods: false + open-redirect: inactive + anti-bot: + injected-URIs: [] + validated-URIs: [] + override-mode: inactive + snort-signatures: + configmap: [] + override-mode: inactive + openapi-schema-validation: + configmap: [] + override-mode: inactive + +log-triggers: + - name: appsec-default-log-trigger + access-control-logging: + allow-events: false + drop-events: true + additional-suspicious-events-logging: + enabled: true + minimum-severity: high + response-body: false + appsec-logging: + all-web-requests: false + detect-events: true + prevent-events: true + extended-logging: + http-headers: false + request-body: false + url-path: false + url-query: false + log-destination: + cloud: false + stdout: + format: json + +custom-responses: + - name: appsec-default-web-user-response + mode: response-code-only + http-response-code: 403 From 36a0a176400ff9640a5e32c6da9b72cdf160788e Mon Sep 17 00:00:00 2001 From: clutat <104208687+clutat@users.noreply.github.com> Date: Tue, 19 Dec 2023 08:54:47 +0100 Subject: [PATCH 22/75] Update README.md - fix directory path in compilation instructions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4f3f854c8..7728b31b3 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ git clone https://github.com/openappsec/open-appsec-npm.git Change into the open-appsec-npm directory: ``` -cd open-appsec-npm/scripts/ci +cd open-appsec-npm ``` Create a folder for the already compiled libraries for the open-appsec attachment: From 24d1e530ae91a297be3a12bfb14bb710a56428b1 Mon Sep 17 00:00:00 2001 From: clutat <104208687+clutat@users.noreply.github.com> Date: Tue, 19 Dec 2023 08:56:13 +0100 Subject: [PATCH 23/75] Update README.md - fix mkdir docker/lib --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7728b31b3..f46d92c50 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ cd open-appsec-npm Create a folder for the already compiled libraries for the open-appsec attachment: ``` -mkdir -r /docker/lib +mkdir docker/lib ``` Copy the following already compiled Attachment files (see "Prerequisites" above) into the docker/lib/ folder: From a909cd1c2e8f611bb8b7bde60f9b88648e29e579 Mon Sep 17 00:00:00 2001 From: clutat <104208687+clutat@users.noreply.github.com> Date: Tue, 19 Dec 2023 11:44:01 +0100 Subject: [PATCH 24/75] Update local_policy.yaml - adjust default log-trigger --- deployment/local_policy.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deployment/local_policy.yaml b/deployment/local_policy.yaml index 8aaf15a86..e288a7a19 100644 --- a/deployment/local_policy.yaml +++ b/deployment/local_policy.yaml @@ -42,6 +42,7 @@ log-triggers: enabled: true minimum-severity: high response-body: false + response-code: true appsec-logging: all-web-requests: false detect-events: true @@ -49,8 +50,8 @@ log-triggers: extended-logging: http-headers: false request-body: false - url-path: false - url-query: false + url-path: true + url-query: true log-destination: cloud: false stdout: From bea44ab697f17b1e4961e91a88095e697161952c Mon Sep 17 00:00:00 2001 From: clutat <104208687+clutat@users.noreply.github.com> Date: Tue, 19 Dec 2023 14:12:36 +0100 Subject: [PATCH 25/75] Update README.md - various updates --- README.md | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f46d92c50..bddfc3e53 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # open-appsec NPM Proxy Manager integration (beta) This is the repository for the beta release of the new integration of open-appsec WAF with NGINX Proxy Manager. -This will allow NGINX Proxy Manager (NPM) users to protect their web applications and web APIs exposed by NGINX Proxy Manager by easily activating and configuring open-appsec protection for each of the configured Proxy Host objects in NPM directly from the NPM Web UI and monitor security events. +This will allow NGINX Proxy Manager (NPM) users to protect their web applications and web APIs exposed by NGINX Proxy Manager by easily activating and configuring open-appsec protection for each of the configured Proxy Host objects in NPM directly from the NPM Web UI and also to monitor security events. +This new integration of open-appsec WAF with NGINX Proxy Manager not only closes the security gap caused by the soon end-of-life ModSecurity WAF, but provides a modern, strong protection alternative in form of open-appsec, a preemptive, machine-learning based, fully automatic WAF that does not rely on signatures at all. ### NGINX Proxy Manager Nginx Proxy Manager is a popular open-source project that simplifies the management of NGINX reverse proxy configurations, offering a user-friendly web-based interface for easy setup and maintenance. It was created by “jc21”. @@ -11,26 +12,38 @@ NGINX Proxy Manager (NPM) is based on NGINX and provided as a container image th NPM itself does not include any WAF solution for effective Threat Prevention against modern attacks or Zero-day attacks. Website and Docs: https://nginxproxymanager.com -Github: https://github.com/NginxProxyManager +Github: https://github.com/NginxProxyManager ### open-appsec WAF: open-appsec WAF provides automatic, preemptive threat prevention for reverse proxies like NGINX. It is machine learning based, which means it doesn’t require signatures (or updating them) at all. This enables it to provide state-of-the art threat prevention even for true zero-day attacks and to significantly reduce administrative effort while strongly reducing the amount of false-positives. open-appsec is a perfect fit to provide advanced threat prevention to the services exposed by NGINX Proxy Manager. -Website: https://www.openappsec.io -Github: https://github.com/openappsec -Docs: https://docs.openappsec.io +Website: https://www.openappsec.io +Github: https://github.com/openappsec +Docs: https://docs.openappsec.io ### Integration of open-appsec WAF with NGINX Proxy Manager: -This new integration not only closes the security gap caused by the soon-end-of-life ModSecurity but provides a modern, strong protection alternative in the form of open-appsec, a preemptive, machine-learning-based, fully automatic WAF that does not rely on signatures at all. -While developing this integration we focused on maximum simplicity to maintain the low entry barrier of the NGINX proxy manager (NPM) project. -The actual deployment of NPM with open-appsec is performed using a slightly enhanced docker-compose file (see below) and configuring open-appsec can be done from an enhanced NPM Web UI interface to which the relevant configuration options for the open-appsec WAF, as well as an option to view the open-appsec logs, were added. + +While developing this integration we focused on maximum simplicity to maintain the low entry barrier as a key design principle of the NGINX proxy manager (NPM) project, which we wanted in the same way to apply also to the addition of open-appsec. + +The actual deployment of NPM with open-appsec is performed using a slightly enhanced docker compose file (see below) which also adds the open-appsec agent container to it, which will perform the actual security inspection. +The NGINX proxy manager container deployed as part of the docker compose is using the “open-appsec-npm” image which is based on the regular NPM code but also adds the open-appsec attachment to it as an NGINX module, which enables the connection between the NGINX and the open-appsec Agent and e.g. provides the HTTP data for inspection to the Agent. +The “open-appsec-npm” container also contains various NPM WebUI enhancements and the integration logic allowing the configuration, administration and monitoring of open-appsec. + +You can read more about open-appsec’s Technology here: +https://www.openappsec.io/tech + +After successful deployment you can then activate and configure open-appsec directly from the enhanced NPM Web UI interface to which the most relevant configuration options for the open-appsec WAF as well as an option to view the open-appsec logs have been added. + +The resulting architecture with the open-appsec Agent container and the NGINX Proxy Manager container then looks like this: + +[TBD ADD ARCHITECTURE IMAGE] Documentation: https://docs.openappsec.io/integrations/nginx-proxy-manager-integration -# Deployment: +# Deployment Step-by-Step: Before you start, make sure to have a Linux environment with Docker and Docker Compose available. To deploy NGINX Proxy Manager with open-appsec integration follow the steps below: 1. Within the directory which you want to use for the deployment: From cbf282ff182e2f6593bcb8b58610362af799516d Mon Sep 17 00:00:00 2001 From: Rami Winestock Date: Tue, 19 Dec 2023 16:26:02 +0200 Subject: [PATCH 26/75] Update UI styles in header, main, and menu components --- frontend/js/app/ui/header/main.ejs | 2 +- frontend/js/app/ui/main.ejs | 2 +- frontend/js/app/ui/menu/main.ejs | 2 +- frontend/scss/custom.scss | 9 +++++++++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/frontend/js/app/ui/header/main.ejs b/frontend/js/app/ui/header/main.ejs index 7467ba1cc..456db4ef7 100644 --- a/frontend/js/app/ui/header/main.ejs +++ b/frontend/js/app/ui/header/main.ejs @@ -1,4 +1,4 @@ -
    +
    -
    +
    diff --git a/frontend/js/app/ui/menu/main.ejs b/frontend/js/app/ui/menu/main.ejs index f978e52de..aec0ddc8c 100644 --- a/frontend/js/app/ui/menu/main.ejs +++ b/frontend/js/app/ui/menu/main.ejs @@ -1,4 +1,4 @@ -
    +