mirror of
https://github.com/netbirdio/plugins.git
synced 2026-05-22 18:44:07 -07:00
www/nginx: add request limits
This commit is contained in:
@@ -305,4 +305,60 @@ class SettingsController extends ApiMutableModelControllerBase
|
||||
{
|
||||
return $this->setBase('security_header', 'security_header', $uuid);
|
||||
}
|
||||
|
||||
// access limit zone headers
|
||||
public function searchlimit_zoneAction()
|
||||
{
|
||||
return $this->searchBase('limit_zone',
|
||||
array('description', 'key', 'size', 'rate', 'rate_unit'));
|
||||
}
|
||||
|
||||
public function getlimit_zoneAction($uuid = null)
|
||||
{
|
||||
$this->sessionClose();
|
||||
return $this->getBase('limit_zone', 'limit_zone', $uuid);
|
||||
}
|
||||
|
||||
public function addlimit_zoneAction()
|
||||
{
|
||||
return $this->addBase('limit_zone', 'limit_zone');
|
||||
}
|
||||
|
||||
public function dellimit_zoneAction($uuid)
|
||||
{
|
||||
return $this->delBase('limit_zone', $uuid);
|
||||
}
|
||||
|
||||
public function setlimit_zoneAction($uuid)
|
||||
{
|
||||
return $this->setBase('limit_zone', 'limit_zone', $uuid);
|
||||
}
|
||||
|
||||
// limit_request_connection
|
||||
public function searchlimit_request_connectionAction()
|
||||
{
|
||||
return $this->searchBase('limit_request_connection',
|
||||
array('description', 'limit_zone', 'nodelay', 'burst', 'connection_count'));
|
||||
}
|
||||
|
||||
public function getlimit_request_connectionAction($uuid = null)
|
||||
{
|
||||
$this->sessionClose();
|
||||
return $this->getBase('limit_request_connection', 'limit_request_connection', $uuid);
|
||||
}
|
||||
|
||||
public function addlimit_request_connectionAction()
|
||||
{
|
||||
return $this->addBase('limit_request_connection', 'limit_request_connection');
|
||||
}
|
||||
|
||||
public function dellimit_request_connectionAction($uuid)
|
||||
{
|
||||
return $this->delBase('limit_request_connection', $uuid);
|
||||
}
|
||||
|
||||
public function setlimit_request_connectionAction($uuid)
|
||||
{
|
||||
return $this->setBase('limit_request_connection', 'limit_request_connection', $uuid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,8 @@ class IndexController extends \OPNsense\Base\IndexController
|
||||
$this->view->naxsi_rule = $this->getForm("naxsi_rule");
|
||||
$this->view->naxsi_custom_policy = $this->getForm("naxsi_custom_policy");
|
||||
$this->view->security_headers = $this->getForm("security_headers");
|
||||
$this->view->limit_request_connection = $this->getForm("limit_request_connection");
|
||||
$this->view->limit_zone = $this->getForm("limit_zone");
|
||||
$this->view->pick('OPNsense/Nginx/index');
|
||||
}
|
||||
|
||||
|
||||
@@ -94,4 +94,11 @@
|
||||
<label>Security Header</label>
|
||||
<type>dropdown</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>httpserver.limit_request_connections</id>
|
||||
<label>Limit Requests</label>
|
||||
<type>select_multiple</type>
|
||||
<style>selectpicker</style>
|
||||
<help>If you choose multiple limits, the strictest will be used.</help>
|
||||
</field>
|
||||
</form>
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<form>
|
||||
<field>
|
||||
<id>limit_request_connection.description</id>
|
||||
<label>Description</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>limit_request_connection.limit_zone</id>
|
||||
<label>Limit Zone</label>
|
||||
<type>dropdown</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>limit_request_connection.connection_count</id>
|
||||
<label>Connection Count (Streams Only)</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>limit_request_connection.burst</id>
|
||||
<label>Burst (HTTP Only)</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>limit_request_connection.nodelay</id>
|
||||
<label>No Delay (HTTP Only)</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
</form>
|
||||
@@ -0,0 +1,27 @@
|
||||
<form>
|
||||
<field>
|
||||
<id>limit_zone.description</id>
|
||||
<label>Description</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>limit_zone.key</id>
|
||||
<label>Key</label>
|
||||
<type>dropdown</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>limit_zone.size</id>
|
||||
<label>Size (MB)</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>limit_zone.rate</id>
|
||||
<label>Rate</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>limit_zone.rate_unit</id>
|
||||
<label>Rate Unit</label>
|
||||
<type>dropdown</type>
|
||||
</field>
|
||||
</form>
|
||||
@@ -61,6 +61,13 @@
|
||||
<style>selectpicker</style>
|
||||
<help>Select an upstream to proxy to or connect via FastCGI if chosen.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>location.limit_request_connections</id>
|
||||
<label>Limit Requests</label>
|
||||
<type>select_multiple</type>
|
||||
<style>selectpicker</style>
|
||||
<help>If you choose multiple limits, the strictest will be used.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>location.root</id>
|
||||
<label>File System Root</label>
|
||||
|
||||
@@ -255,6 +255,18 @@
|
||||
<php_override_scriptname type="TextField">
|
||||
<Required>N</Required>
|
||||
</php_override_scriptname>
|
||||
<limit_request_connections type="ModelRelationField">
|
||||
<Model>
|
||||
<template>
|
||||
<source>OPNsense.Nginx.Nginx</source>
|
||||
<items>limit_request_connection</items>
|
||||
<display>description</display>
|
||||
</template>
|
||||
</Model>
|
||||
<ValidationMessage>Selected limit zone not found</ValidationMessage>
|
||||
<Required>N</Required>
|
||||
<multiple>Y</multiple>
|
||||
</limit_request_connections>
|
||||
</location>
|
||||
|
||||
<custom_policy type="ArrayField">
|
||||
@@ -479,6 +491,18 @@
|
||||
<Required>N</Required>
|
||||
<multiple>N</multiple>
|
||||
</security_header>
|
||||
<limit_request_connections type="ModelRelationField">
|
||||
<Model>
|
||||
<template>
|
||||
<source>OPNsense.Nginx.Nginx</source>
|
||||
<items>limit_request_connection</items>
|
||||
<display>description</display>
|
||||
</template>
|
||||
</Model>
|
||||
<ValidationMessage>Selected limit zone not found</ValidationMessage>
|
||||
<Required>N</Required>
|
||||
<multiple>Y</multiple>
|
||||
</limit_request_connections>
|
||||
</http_server>
|
||||
|
||||
<http_rewrite type="ArrayField">
|
||||
@@ -835,5 +859,68 @@
|
||||
<default>0</default>
|
||||
</csp_form_action_none>
|
||||
</security_header>
|
||||
|
||||
<limit_zone type="ArrayField">
|
||||
<description type="TextField">
|
||||
<Required>Y</Required>
|
||||
</description>
|
||||
<key type="OptionField">
|
||||
<default>binary_remote_addr</default>
|
||||
<OptionValues>
|
||||
<binary_remote_addr>Remote IP Address</binary_remote_addr>
|
||||
</OptionValues>
|
||||
<Required>Y</Required>
|
||||
</key>
|
||||
<rate_unit type="OptionField">
|
||||
<default>r/s</default>
|
||||
<OptionValues>
|
||||
<val1 value="r/s">Requests Per Second</val1>
|
||||
<val2 value="r/m">Requests Per Minute</val2>
|
||||
</OptionValues>
|
||||
<Required>Y</Required>
|
||||
</rate_unit>
|
||||
<size type="IntegerField">
|
||||
<Required>Y</Required>
|
||||
<default>10</default>
|
||||
<MinimumValue>1</MinimumValue>
|
||||
</size>
|
||||
<rate type="IntegerField">
|
||||
<Required>Y</Required>
|
||||
<default>20</default>
|
||||
<MinimumValue>1</MinimumValue>
|
||||
</rate>
|
||||
</limit_zone>
|
||||
|
||||
<limit_request_connection type="ArrayField">
|
||||
<limit_zone type="ModelRelationField">
|
||||
<Model>
|
||||
<template>
|
||||
<source>OPNsense.Nginx.Nginx</source>
|
||||
<items>limit_zone</items>
|
||||
<display>description</display>
|
||||
</template>
|
||||
</Model>
|
||||
<ValidationMessage>Selected limit zone not found</ValidationMessage>
|
||||
<Required>Y</Required>
|
||||
<multiple>Y</multiple>
|
||||
</limit_zone>
|
||||
<connection_count type="IntegerField">
|
||||
<MinimumValue>1</MinimumValue>
|
||||
<Required>Y</Required>
|
||||
<default>5</default>
|
||||
</connection_count>
|
||||
<burst type="IntegerField">
|
||||
<MinimumValue>1</MinimumValue>
|
||||
<Required>N</Required>
|
||||
<default>20</default>
|
||||
</burst>
|
||||
<nodelay type="BooleanField">
|
||||
<default>1</default>
|
||||
<Required>Y</Required>
|
||||
</nodelay>
|
||||
<description type="TextField">
|
||||
<Required>Y</Required>
|
||||
</description>
|
||||
</limit_request_connection>
|
||||
</items>
|
||||
</model>
|
||||
|
||||
@@ -97,6 +97,8 @@ $( document ).ready(function() {
|
||||
'httprewrite',
|
||||
'custompolicy',
|
||||
'security_header',
|
||||
'limit_zone',
|
||||
'limit_request_connection',
|
||||
'naxsirule'].forEach(function(element) {
|
||||
$("#grid-" + element).UIBootgrid(
|
||||
{ 'search':'/api/nginx/settings/search' + element,
|
||||
@@ -118,10 +120,14 @@ $( document ).ready(function() {
|
||||
<ul class="nav nav-tabs" role="tablist" id="maintabs">
|
||||
{{ partial("layout_partials/base_tabs_header",['formData':settings]) }}
|
||||
<li role="presentation" class="dropdown">
|
||||
<a data-toggle="dropdown" href="#" class="dropdown-toggle pull-right visible-lg-inline-block visible-md-inline-block visible-xs-inline-block visible-sm-inline-block" role="button">
|
||||
<a data-toggle="dropdown" href="#"
|
||||
class="dropdown-toggle pull-right visible-lg-inline-block visible-md-inline-block visible-xs-inline-block visible-sm-inline-block"
|
||||
role="button">
|
||||
<b><span class="caret"></span></b>
|
||||
</a>
|
||||
<a data-toggle="tab" onclick="$('#subtab_item_nginx-http-location').click();" class="visible-lg-inline-block visible-md-inline-block visible-xs-inline-block visible-sm-inline-block" style="border-right:0px;"><b>{{ lang._('HTTP(S)')}}</b></a>
|
||||
<a data-toggle="tab" onclick="$('#subtab_item_nginx-http-location').click();"
|
||||
class="visible-lg-inline-block visible-md-inline-block visible-xs-inline-block visible-sm-inline-block"
|
||||
style="border-right:0px;"><b>{{ lang._('HTTP(S)')}}</b></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li>
|
||||
<a data-toggle="tab" id="subtab_item_nginx-http-location" href="#subtab_nginx-http-location">{{ lang._('Location')}}</a>
|
||||
@@ -155,6 +161,25 @@ $( document ).ready(function() {
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li role="presentation" class="dropdown">
|
||||
<a data-toggle="dropdown"
|
||||
href="#"
|
||||
class="dropdown-toggle pull-right visible-lg-inline-block visible-md-inline-block visible-xs-inline-block visible-sm-inline-block"
|
||||
role="button">
|
||||
<b><span class="caret"></span></b>
|
||||
</a>
|
||||
<a data-toggle="tab" onclick="$('#subtab_item_nginx-access-request-limit').click();"
|
||||
class="visible-lg-inline-block visible-md-inline-block visible-xs-inline-block visible-sm-inline-block"
|
||||
style="border-right:0px;"><b>{{ lang._('Access')}}</b></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li>
|
||||
<a data-toggle="tab" id="subtab_item_nginx-access-request-limit" href="#subtab_nginx-access-request-limit">{{ lang._('Limit Zone')}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a data-toggle="tab" id="subtab_item_nginx-access-request-limit-connection" href="#subtab_nginx-access-request-limit-connection">{{ lang._('Connection Limits')}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="content-box tab-content">
|
||||
@@ -392,6 +417,56 @@ $( document ).ready(function() {
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<div id="subtab_nginx-access-request-limit" class="tab-pane fade">
|
||||
<table id="grid-limit_zone" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="limit_zonedlg">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="description" data-type="string" data-sortable="true" data-visible="true">{{ lang._('Description') }}</th>
|
||||
<th data-column-id="key" data-type="string" data-sortable="true" data-visible="true">{{ lang._('Key') }}</th>
|
||||
<th data-column-id="size" data-type="string" data-sortable="true" data-visible="true">{{ lang._('Size') }}</th>
|
||||
<th data-column-id="rate" data-type="string" data-sortable="true" data-visible="true">{{ lang._('Rate') }}</th>
|
||||
<th data-column-id="rate_unit" data-type="string" data-sortable="true" data-visible="true">{{ lang._('Rate Unit') }}</th>
|
||||
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<button data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
|
||||
<button type="button" class="btn btn-xs reload_btn btn-primary"><span class="fa fa-refresh reloadAct_progress"></span></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<div id="subtab_nginx-access-request-limit-connection" class="tab-pane fade">
|
||||
<table id="grid-limit_request_connection" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="limit_request_connectiondlg">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="description" data-type="string" data-sortable="true" data-visible="true">{{ lang._('Description') }}</th>
|
||||
<th data-column-id="limit_zone" data-type="string" data-sortable="true" data-visible="true">{{ lang._('Limit Zone') }}</th>
|
||||
<th data-column-id="connection_count" data-type="string" data-sortable="true" data-visible="true">{{ lang._('Connection Count') }}</th>
|
||||
<th data-column-id="burst" data-type="string" data-sortable="true" data-visible="true">{{ lang._('Burst') }}</th>
|
||||
<th data-column-id="nodelay" data-type="string" data-sortable="true" data-visible="true">{{ lang._('No Delay') }}</th>
|
||||
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<button data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
|
||||
<button type="button" class="btn btn-xs reload_btn btn-primary"><span class="fa fa-refresh reloadAct_progress"></span></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -406,3 +481,5 @@ $( document ).ready(function() {
|
||||
{{ partial("layout_partials/base_dialog",['fields': naxsi_custom_policy,'id':'custompolicydlg', 'label':lang._('Edit WAF Policy')]) }}
|
||||
{{ partial("layout_partials/base_dialog",['fields': naxsi_rule,'id':'naxsiruledlg', 'label':lang._('Edit Naxsi Rule')]) }}
|
||||
{{ partial("layout_partials/base_dialog",['fields': security_headers,'id':'security_headersdlg', 'label':lang._('Edit Security Headers')]) }}
|
||||
{{ partial("layout_partials/base_dialog",['fields': limit_request_connection,'id':'limit_request_connectiondlg', 'label':lang._('Edit Request Connection Limit')]) }}
|
||||
{{ partial("layout_partials/base_dialog",['fields': limit_zone,'id':'limit_zonedlg', 'label':lang._('Edit Limit Zone')]) }}
|
||||
|
||||
@@ -32,6 +32,11 @@ keepalive_timeout {{ OPNsense.Nginx.http.keepalive_timeout }};
|
||||
# TODO add when core is ready for allowing nginx to serve the web interface
|
||||
# include nginx_web.conf;
|
||||
|
||||
|
||||
{% for zone in helpers.toList('OPNsense.Nginx.limit_zone') %}
|
||||
limit_req_zone ${{ zone.key }} zone={{ zone['@uuid'].replace('-', '') }}:{{ zone.size }}m rate={{ zone.rate }}{{ zone.rate_unit }};
|
||||
{% endfor %}
|
||||
|
||||
{% include "OPNsense/Nginx/upstream.conf" ignore missing with context %}
|
||||
|
||||
{% set listen_list = [] %}
|
||||
@@ -91,6 +96,10 @@ server {
|
||||
{% if security_rule is defined %}
|
||||
{% include "OPNsense/Nginx/security_rule.conf" ignore missing with context %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if server.limit_request_connections is defined %}
|
||||
{% set limit_request = server.limit_request_connections %}
|
||||
{% include "OPNsense/Nginx/httprequestlimit.conf" ignore missing with context %}
|
||||
{% endif %}
|
||||
# location to ban the host permanently
|
||||
set $naxsi_extensive_log {% if server.naxsi_extensive_log is defined and server.naxsi_extensive_log == '1' %}1{% else %}0{% endif %};
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{% if limit_request is defined and limit_request != '' %}
|
||||
{% for req in limit_request.split(',') %}
|
||||
{% if helpers.getUUID(req) is defined %}
|
||||
{% set req_data = helpers.getUUID(req) %}
|
||||
{% if helpers.getUUID(req_data.limit_zone) is defined %}
|
||||
limit_req zone={{ req_data.limit_zone.replace('-', '')
|
||||
}}{% if req_data.burst is defined and req_data.burst != '' %} burst={{ req_data.burst
|
||||
}}{% if req_data.nodelay is defined and req_data.nodelay == '1' %} nodelay{% endif %}{% endif %};
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
@@ -47,6 +47,10 @@ location {{ location.matchtype }} {{ location.urlpattern }} {
|
||||
{% else %}
|
||||
autoindex off;
|
||||
{% endif %}
|
||||
{% if location.limit_request_connections is defined %}
|
||||
{% set limit_request = location.limit_request_connections %}
|
||||
{% include "OPNsense/Nginx/httprequestlimit.conf" ignore missing with context %}
|
||||
{% endif %}
|
||||
{% if location.authbasic is defined and location.authbasicuserfile is defined %}
|
||||
auth_basic "{{location.authbasic}}";
|
||||
auth_basic_user_file /var/db/nginx/auth/{{ location.authbasicuserfile }};
|
||||
|
||||
Reference in New Issue
Block a user