diff --git a/README.md b/README.md
index 1d02f812e..c2ee0de3a 100644
--- a/README.md
+++ b/README.md
@@ -81,6 +81,7 @@ sysutils/vmware -- VMware tools
sysutils/xen -- Xen guest utilities
www/c-icap -- c-icap connects the web proxy with a virus scanner
www/cache -- Webserver cache
+www/nginx -- Nginx HTTP server and reverse proxy
www/web-proxy-sso -- Kerberos authentication module
www/web-proxy-useracl -- Group and user ACL for the web proxy
```
diff --git a/www/nginx/Makefile b/www/nginx/Makefile
new file mode 100644
index 000000000..849242b50
--- /dev/null
+++ b/www/nginx/Makefile
@@ -0,0 +1,8 @@
+PLUGIN_NAME= nginx
+PLUGIN_VERSION= 0.2
+PLUGIN_COMMENT= Nginx HTTP server and reverse proxy
+PLUGIN_DEPENDS= nginx
+PLUGIN_MAINTAINER= franz.fabian.94@gmail.com
+PLUGIN_DEVEL= yes
+
+.include "../../Mk/plugins.mk"
diff --git a/www/nginx/pkg-descr b/www/nginx/pkg-descr
new file mode 100644
index 000000000..3f1a716e4
--- /dev/null
+++ b/www/nginx/pkg-descr
@@ -0,0 +1,8 @@
+NGINX is a high performance edge web server with the lowest memory footprint
+and the key features to build modern and efficient web infrastructure.
+
+NGINX functionality includes HTTP server, HTTP and mail reverse proxy, caching,
+load balancing, compression, request throttling, connection multiplexing and
+reuse, SSL offload and HTTP media streaming.
+
+WWW: https://nginx.org/
diff --git a/www/nginx/src/etc/nginx/views/opnsense_error_404.html b/www/nginx/src/etc/nginx/views/opnsense_error_404.html
new file mode 100644
index 000000000..54200472c
--- /dev/null
+++ b/www/nginx/src/etc/nginx/views/opnsense_error_404.html
@@ -0,0 +1,23 @@
+
+
+
+
+ Request Denied
+
+
+
+
+
+Not Found
+The resource you want to access is not available.
+Please contact the webmaster if you think this is an error.
+
+
diff --git a/www/nginx/src/etc/nginx/views/opnsense_server_error.html b/www/nginx/src/etc/nginx/views/opnsense_server_error.html
new file mode 100644
index 000000000..6a3267f4a
--- /dev/null
+++ b/www/nginx/src/etc/nginx/views/opnsense_server_error.html
@@ -0,0 +1,23 @@
+
+
+
+
+ Request Denied
+
+
+
+
+
+Server Error
+Sorry, but something went wrong on our side.
+There is nothing you can do except waiting until we fix the issue.
+
+
diff --git a/www/nginx/src/etc/nginx/views/waf_denied.html b/www/nginx/src/etc/nginx/views/waf_denied.html
new file mode 100644
index 000000000..b5e96d2c9
--- /dev/null
+++ b/www/nginx/src/etc/nginx/views/waf_denied.html
@@ -0,0 +1,60 @@
+
+
+
+
+ Request Denied
+
+
+
+
+
+ Request Denied For Security Reasons
+ The request has been denied by the web application firewall due to a security policy violation.
+ Request information have been logged to investigate the incident.
+ If you think this is an error on our side, please contact us.
+
+
+
+
+
+
+
diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/Api/ServiceController.php b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/Api/ServiceController.php
new file mode 100644
index 000000000..77c0ef122
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/Api/ServiceController.php
@@ -0,0 +1,103 @@
+
+ * Copyright (C) 2016 IT-assistans Sverige AB
+ * Copyright (C) 2015-2016 Deciso B.V.
+ * Copyright (C) 2018 Fabian Franz
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+namespace OPNsense\Nginx\Api;
+
+use OPNsense\Base\ApiMutableServiceControllerBase;
+use OPNsense\Core\Backend;
+
+class ServiceController extends ApiMutableServiceControllerBase
+{
+ static protected $internalServiceClass = '\OPNsense\Nginx\Nginx';
+ static protected $internalServiceTemplate = 'OPNsense/Nginx';
+ static protected $internalServiceEnabled = 'general.enabled';
+ static protected $internalServiceName = 'nginx';
+
+ /**
+ * override parent method - stopping nginx is not allowed because otherwise you would loose
+ * access to the web interface
+ */
+ public function stopAction()
+ {
+ return array('status' => 'failed');
+ }
+
+
+ /**
+ * reconfigure with optional stop, generate config and start / reload
+ * @return array response message
+ * @throws \Exception when configd action fails
+ * @throws \ReflectionException when model can't be instantiated
+ */
+ public function reconfigureAction()
+ {
+ if ($this->request->isPost()) {
+ $this->sessionClose();
+ $model = $this->getModel();
+ $backend = new Backend();
+ if ($this->reconfigureForceRestart()) {
+ $backend->configdRun('nginx stop');
+ }
+ $backend->configdRun('template reload OPNsense/Nginx');
+ $runStatus = $this->statusAction();
+ if ($runStatus['status'] != 'running') {
+ $backend->configdRun('nginx start');
+ } else {
+ $backend->configdRun('nginx reload');
+ }
+ return array('status' => 'ok');
+ } else {
+ return array('status' => 'failed');
+ }
+ }
+
+ /**
+ * retrieve status of service
+ * @return array response message
+ * @throws \Exception when configd action fails
+ */
+ public function statusAction()
+ {
+ $backend = new Backend();
+ $model = $this->getModel();
+ $response = $backend->configdRun('nginx status');
+
+ if (strpos($response, 'not running') > 0) {
+ $status = 'stopped';
+ } elseif (strpos($response, 'is running') > 0) {
+ $status = 'running';
+ } else {
+ $status = 'unknown';
+ }
+
+ return array('status' => $status);
+ }
+}
diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/Api/SettingsController.php b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/Api/SettingsController.php
new file mode 100644
index 000000000..28f5eb716
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/Api/SettingsController.php
@@ -0,0 +1,281 @@
+searchBase('userlist', array('name', 'users'));
+ }
+
+ public function getuserlistAction($uuid = null)
+ {
+ $this->sessionClose();
+ return $this->getBase('userlist', 'userlist', $uuid);
+ }
+
+ public function adduserlistAction()
+ {
+ return $this->addBase('userlist', 'userlist');
+ }
+
+ public function deluserlistAction($uuid)
+ {
+ return $this->delBase('userlist', $uuid);
+ }
+
+ public function setuserlistAction($uuid)
+ {
+ return $this->setBase('userlist', 'userlist', $uuid);
+ }
+
+ // Credential
+ public function searchcredentialAction()
+ {
+ return $this->searchBase('credential', array('username'));
+ }
+
+ public function getcredentialAction($uuid = null)
+ {
+ $this->sessionClose();
+ return $this->getBase('credential', 'credential', $uuid);
+ }
+
+ public function addcredentialAction()
+ {
+ return $this->addBase('credential', 'credential');
+ }
+
+ public function delcredentialAction($uuid)
+ {
+ return $this->delBase('credential', $uuid);
+ }
+
+ public function setcredentialAction($uuid)
+ {
+ return $this->setBase('credential', 'credential', $uuid);
+ }
+
+ // Upstream
+ public function searchupstreamAction()
+ {
+ return $this->searchBase('upstream', array('description', 'serverentries'));
+ }
+
+ public function getupstreamAction($uuid = null)
+ {
+ $this->sessionClose();
+ return $this->getBase('upstream', 'upstream', $uuid);
+ }
+
+ public function addupstreamAction()
+ {
+ return $this->addBase('upstream', 'upstream');
+ }
+
+ public function delupstreamAction($uuid)
+ {
+ return $this->delBase('upstream', $uuid);
+ }
+
+ public function setupstreamAction($uuid)
+ {
+ return $this->setBase('upstream', 'upstream', $uuid);
+ }
+
+ // Upstream Server
+ public function searchupstreamserverAction()
+ {
+ return $this->searchBase('upstream_server', array('description', 'server', 'priority'));
+ }
+
+ public function getupstreamserverAction($uuid = null)
+ {
+ $this->sessionClose();
+ return $this->getBase('upstream_server', 'upstream_server', $uuid);
+ }
+
+ public function addupstreamserverAction()
+ {
+ return $this->addBase('upstream_server', 'upstream_server');
+ }
+
+ public function delupstreamserverAction($uuid)
+ {
+ return $this->delBase('upstream_server', $uuid);
+ }
+
+ public function setupstreamserverAction($uuid)
+ {
+ return $this->setBase('upstream_server', 'upstream_server', $uuid);
+ }
+
+ // Location
+ public function searchlocationAction()
+ {
+ return $this->searchBase('location', array('description','urlpattern', 'matchtype', 'enable_secrules', 'force_https'));
+ }
+
+ public function getlocationAction($uuid = null)
+ {
+ $this->sessionClose();
+ return $this->getBase('location', 'location', $uuid);
+ }
+
+ public function addlocationAction()
+ {
+ return $this->addBase('location', 'location');
+ }
+
+ public function dellocationAction($uuid)
+ {
+ return $this->delBase('location', $uuid);
+ }
+
+ public function setlocationAction($uuid)
+ {
+ return $this->setBase('location', 'location', $uuid);
+ }
+
+ // Custom Policy
+ public function searchcustompolicyAction()
+ {
+ return $this->searchBase('custom_policy', array('name', 'operator', 'value', 'action'));
+ }
+
+ public function getcustompolicyAction($uuid = null)
+ {
+ $this->sessionClose();
+ return $this->getBase('custompolicy', 'custom_policy', $uuid);
+ }
+
+ public function addcustompolicyAction()
+ {
+ return $this->addBase('custompolicy', 'custom_policy');
+ }
+
+ public function delcustompolicyAction($uuid)
+ {
+ return $this->delBase('custom_policy', $uuid);
+ }
+
+ public function setcustompolicyAction($uuid)
+ {
+ return $this->setBase('custompolicy', 'custom_policy', $uuid);
+ }
+
+ // http server
+ public function searchhttpserverAction()
+ {
+ return $this->searchBase('http_server', array('servername', 'https_only', 'certificate', 'listen_http_port', 'listen_https_port'));
+ }
+
+ public function gethttpserverAction($uuid = null)
+ {
+ $this->sessionClose();
+ return $this->getBase('httpserver', 'http_server', $uuid);
+ }
+
+ public function addhttpserverAction()
+ {
+ return $this->addBase('httpserver', 'http_server');
+ }
+
+ public function delhttpserverAction($uuid)
+ {
+ return $this->delBase('http_server', $uuid);
+ }
+
+ public function sethttpserverAction($uuid)
+ {
+ return $this->setBase('httpserver', 'http_server', $uuid);
+ }
+
+ // naxsi rules
+ public function searchnaxsiruleAction()
+ {
+ return $this->searchBase('naxsi_rule', array('description', 'ruletype', 'message'));
+ }
+
+ public function getnaxsiruleAction($uuid = null)
+ {
+ $this->sessionClose();
+ return $this->getBase('naxsi_rule', 'naxsi_rule', $uuid);
+ }
+
+ public function addnaxsiruleAction()
+ {
+ return $this->addBase('naxsi_rule', 'naxsi_rule');
+ }
+
+ public function delnaxsiruleAction($uuid)
+ {
+ return $this->delBase('naxsi_rule', $uuid);
+ }
+
+ public function setnaxsiruleAction($uuid)
+ {
+ return $this->setBase('naxsi_rule', 'naxsi_rule', $uuid);
+ }
+
+ // http url rewriting
+ public function searchhttprewriteAction()
+ {
+ return $this->searchBase('http_rewrite', array('description', 'source', 'destination', 'flag'));
+ }
+
+ public function gethttprewriteAction($uuid = null)
+ {
+ $this->sessionClose();
+ return $this->getBase('httprewrite', 'http_rewrite', $uuid);
+ }
+
+ public function addhttprewriteAction()
+ {
+ return $this->addBase('httprewrite', 'http_rewrite');
+ }
+
+ public function delhttprewriteAction($uuid)
+ {
+ return $this->delBase('http_rewrite', $uuid);
+ }
+
+ public function sethttprewriteAction($uuid)
+ {
+ return $this->setBase('httprewrite', 'http_rewrite', $uuid);
+ }
+}
diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/IndexController.php b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/IndexController.php
new file mode 100644
index 000000000..d6a445284
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/IndexController.php
@@ -0,0 +1,54 @@
+view->settings = $this->getForm("settings");
+ $this->view->upstream_server = $this->getForm("upstream_server");
+ $this->view->upstream = $this->getForm("upstream");
+ $this->view->location = $this->getForm("location");
+ $this->view->credential = $this->getForm("credential");
+ $this->view->userlist = $this->getForm("userlist");
+ $this->view->httpserver = $this->getForm("httpserver");
+ $this->view->httprewrite = $this->getForm("httprewrite");
+ $this->view->naxsi_rule = $this->getForm("naxsi_rule");
+ $this->view->naxsi_custom_policy = $this->getForm("naxsi_custom_policy");
+ $this->view->pick('OPNsense/Nginx/index');
+ }
+}
diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/credential.xml b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/credential.xml
new file mode 100644
index 000000000..a014c5708
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/credential.xml
@@ -0,0 +1,12 @@
+
diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/httprewrite.xml b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/httprewrite.xml
new file mode 100644
index 000000000..eda6fc20c
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/httprewrite.xml
@@ -0,0 +1,27 @@
+
diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/httpserver.xml b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/httpserver.xml
new file mode 100644
index 000000000..1a455d735
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/httpserver.xml
@@ -0,0 +1,92 @@
+
diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/location.xml b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/location.xml
new file mode 100644
index 000000000..cc41d9c8a
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/location.xml
@@ -0,0 +1,106 @@
+
diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/naxsi_custom_policy.xml b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/naxsi_custom_policy.xml
new file mode 100644
index 000000000..26f6fe026
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/naxsi_custom_policy.xml
@@ -0,0 +1,31 @@
+
diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/naxsi_rule.xml b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/naxsi_rule.xml
new file mode 100644
index 000000000..cdaec08a4
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/naxsi_rule.xml
@@ -0,0 +1,110 @@
+
diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/settings.xml b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/settings.xml
new file mode 100644
index 000000000..94100b302
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/settings.xml
@@ -0,0 +1,61 @@
+
diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/upstream.xml b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/upstream.xml
new file mode 100644
index 000000000..4b8092077
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/upstream.xml
@@ -0,0 +1,13 @@
+
diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/upstream_server.xml b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/upstream_server.xml
new file mode 100644
index 000000000..655ad6c3d
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/upstream_server.xml
@@ -0,0 +1,42 @@
+
diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/userlist.xml b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/userlist.xml
new file mode 100644
index 000000000..32dc6d074
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/userlist.xml
@@ -0,0 +1,12 @@
+
diff --git a/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/ACL/ACL.xml b/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/ACL/ACL.xml
new file mode 100644
index 000000000..8793dd4bd
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/ACL/ACL.xml
@@ -0,0 +1,9 @@
+
+
+ nginx
+
+ ui/nginx/*
+ api/nginx/*
+
+
+
diff --git a/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/Menu/Menu.xml b/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/Menu/Menu.xml
new file mode 100644
index 000000000..3c4c31b12
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/Menu/Menu.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/Nginx.php b/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/Nginx.php
new file mode 100644
index 000000000..cfed5580e
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/Nginx.php
@@ -0,0 +1,34 @@
+
+ //OPNsense/Nginx
+ nginx web server, reverse proxy and waf
+
+
+
+ 0
+ Y
+
+
+
+
+
+ 0
+ Y
+
+
+
+
+
+ 0
+ N
+
+
+ 60
+ N
+
+
+ N
+
+
+
+
+
+ Y
+
+
+
+
+ OPNsense.Nginx.Nginx
+ credential
+ username
+
+
+ Selected user not found
+ Y
+ Y
+
+
+
+
+
+ Y
+
+
+ Y
+
+
+
+
+
+ Y
+
+
+
+
+ OPNsense.Nginx.Nginx
+ upstream_server
+ description
+
+
+ Selected server not found
+ Y
+ Y
+
+
+
+
+
+ Y
+
+
+ Y
+
+
+ Y
+
+
+ 0
+ Y
+
+
+ N
+
+
+ N
+
+
+ N
+
+
+
+ Permanently Unreachable
+ Backup Server
+
+ N
+
+
+
+
+
+ Y
+
+
+ Y
+
+
+
+ Exact Match ("=")
+ Case Sensitive Match ("~")
+ Case Insensitive Match ("~*")
+ Don't check regular expressions on logest prefix match ("^~")
+
+ N
+
+
+ 0
+ Y
+
+
+ 0
+ Y
+
+
+ N
+
+
+ N
+
+
+
+
+ OPNsense.Nginx.Nginx
+ custom_policy
+ name
+
+
+ Selected server not found
+ N
+ Y
+
+
+
+
+ OPNsense.Nginx.Nginx
+ upstream
+ description
+
+
+ Selected upstream not found
+ N
+ N
+
+
+ N
+
+
+
+
+ OPNsense.Nginx.Nginx
+ http_rewrite
+ description
+
+
+ Selected rewrite(s) not found
+ N
+ Y
+
+
+ N
+
+
+ N
+
+
+ N
+
+
+
+
+ OPNsense.Nginx.Nginx
+ userlist
+ name
+
+
+ Selected server not found
+ N
+ Y
+
+
+ 0
+ Y
+
+
+ N
+
+
+
+
+
+ Y
+
+
+
+
+ OPNsense.Nginx.Nginx
+ naxsi_rule
+ description
+
+
+ Selected rule not found
+ Y
+ Y
+
+
+ Y
+
+
+ Y
+ >=
+
+ Bigger or Equal
+ Bigger
+ Lesser
+ Lesser or Equal
+ Equal
+
+
+
+ Y
+ BLOCK
+
+ Block Request
+ Allow Request
+ Drop The Connection
+ Log Request
+
+
+
+
+
+
+ Y
+
+
+
+ Main Rule
+ Basic Rule
+
+ Y
+
+
+ Y
+ /^[^"]+$/
+
+
+ Y
+ 1000
+
+
+ N
+ /^[^"]+$/
+
+
+ N
+ /^[^"]+$/
+
+
+ Y
+ /^[^"]+$/
+
+
+ Y
+ id
+
+ Blacklist
+ Whitelist
+
+
+
+ Y
+
+
+ Y
+ 8
+
+
+ Y
+
+
+ Y
+
+
+ Y
+
+
+ /^[^"]+$/
+
+
+ /^[^"]+$/
+
+
+ /^[^"]+$/
+
+
+ Y
+
+
+ Y
+
+
+ Y
+
+
+
+
+
+ Y
+
+
+ N
+ 80
+
+
+ N
+ 443
+
+
+
+
+ OPNsense.Nginx.Nginx
+ location
+ description
+
+
+ Selected location(s) not found
+ N
+ Y
+
+
+
+
+ OPNsense.Nginx.Nginx
+ http_rewrite
+ description
+
+
+ Selected rewrite(s) not found
+ N
+ Y
+
+
+ N
+
+
+ cert
+ N
+
+
+ ca
+ N
+
+
+ Off
+
+ Off
+ On
+ Optional
+ Optional, don't verify
+
+ Y
+
+
+ main
+
+ Default
+ Anonymized
+ Disabled
+
+ Y
+
+
+ Y
+ 1
+
+
+ utf-8
+
+ utf-8
+
+ N
+
+
+ 0
+ Y
+
+
+ 0
+ Y
+
+
+ 0
+ Y
+
+
+ 1
+ Y
+
+
+
+
+
+ Y
+ /^[^" \t]+$/i
+
+
+ Y
+
+
+ Y
+ /^[^" \t]+$/i
+
+
+
+ Stop processing rules
+ Stop processing rules and find location
+ Redirect
+ Permanent
+
+ N
+
+
+
+
+
diff --git a/www/nginx/src/opnsense/mvc/app/views/OPNsense/Nginx/index.volt b/www/nginx/src/opnsense/mvc/app/views/OPNsense/Nginx/index.volt
new file mode 100644
index 000000000..b1d8109f9
--- /dev/null
+++ b/www/nginx/src/opnsense/mvc/app/views/OPNsense/Nginx/index.volt
@@ -0,0 +1,382 @@
+{#
+ # Copyright (C) 2017-2018 Fabian Franz
+ # Copyright (C) 2014-2015 Deciso B.V.
+ # All rights reserved.
+ #
+ # Redistribution and use in source and binary forms, with or without
+ # modification, are permitted provided that the following conditions are met:
+ #
+ # 1. Redistributions of source code must retain the above copyright notice,
+ # this list of conditions and the following disclaimer.
+ #
+ # 2. Redistributions in binary form must reproduce the above copyright
+ # notice, this list of conditions and the following disclaimer in the
+ # documentation and/or other materials provided with the distribution.
+ #
+ # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ # AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ # POSSIBILITY OF SUCH DAMAGE.
+ #}
+
+
+
+
+
+ {{ partial("layout_partials/base_tabs_header",['formData':settings]) }}
+
+
+
+
+ {{ lang._('HTTP(S)')}}
+
+
+
+
+
+ {{ partial("layout_partials/base_tabs_content",['formData':settings]) }}
+
+
+
+
+ {{ lang._('Description') }}
+ {{ lang._('URL Pattern') }}
+ {{ lang._('Match Type') }}
+ {{ lang._('WAF Enabled') }}
+ {{ lang._('Force HTTPS') }}
+ {{ lang._('Commands') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ lang._('Description') }}
+ {{ lang._('Server') }}
+ {{ lang._('Priority') }}
+ {{ lang._('Commands') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ lang._('Description') }}
+ {{ lang._('Servers') }}
+ {{ lang._('Commands') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ lang._('Username') }}
+ {{ lang._('Commands') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ lang._('Name') }}
+ {{ lang._('Users') }}
+ {{ lang._('Commands') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ lang._('Servername') }}
+ {{ lang._('HTTPS Only') }}
+ {{ lang._('Certificate') }}
+ {{ lang._('HTTP Port') }}
+ {{ lang._('HTTPS Port') }}
+ {{ lang._('Commands') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ lang._('Description') }}
+ {{ lang._('Source URL') }}
+ {{ lang._('Destination URL') }}
+ {{ lang._('Flag') }}
+ {{ lang._('Commands') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ lang._('Name') }}
+ {{ lang._('Operator') }}
+ {{ lang._('Value') }}
+ {{ lang._('Action') }}
+ {{ lang._('Commands') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ lang._('Description') }}
+ {{ lang._('Rule Type') }}
+ {{ lang._('Message') }}
+ {{ lang._('Commands') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{{ partial("layout_partials/base_dialog",['fields': upstream,'id':'upstreamdlg', 'label':lang._('Edit Upstream')]) }}
+{{ partial("layout_partials/base_dialog",['fields': upstream_server,'id':'upstreamserverdlg', 'label':lang._('Edit Upstream')]) }}
+{{ partial("layout_partials/base_dialog",['fields': location,'id':'locationdlg', 'label':lang._('Edit Location')]) }}
+{{ partial("layout_partials/base_dialog",['fields': credential,'id':'credentialdlg', 'label':lang._('Edit Credential')]) }}
+{{ partial("layout_partials/base_dialog",['fields': userlist,'id':'userlistdlg', 'label':lang._('Edit User List')]) }}
+{{ partial("layout_partials/base_dialog",['fields': httpserver,'id':'httpserverdlg', 'label':lang._('Edit HTTP Server')]) }}
+{{ partial("layout_partials/base_dialog",['fields': httprewrite,'id':'httprewritedlg', 'label':lang._('Edit URL Rewrite')]) }}
+{{ 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')]) }}
diff --git a/www/nginx/src/opnsense/scripts/nginx/ngx_auth.php b/www/nginx/src/opnsense/scripts/nginx/ngx_auth.php
new file mode 100644
index 000000000..a1e166a17
--- /dev/null
+++ b/www/nginx/src/opnsense/scripts/nginx/ngx_auth.php
@@ -0,0 +1,55 @@
+get($auth_server);
+ return $authenticator->authenticate($username, $password);
+}
+
+function password_auth($auth_server = 'Local Database') {
+ if (!isset($_SERVER['PHP_AUTH_PW']) || !isset($_SERVER['PHP_AUTH_PW'])) return false;
+ return password_auth_test($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'], $auth_server);
+}
+
+if (password_auth()) {
+ header("HTTP/1.1 200 OK");
+} else {
+ header("HTTP/1.1 401 Authorization Required");
+ header('WWW-Authenticate: Basic realm="OPNsense Protected Area - Authentication Required"');
+}
diff --git a/www/nginx/src/opnsense/scripts/nginx/setup.php b/www/nginx/src/opnsense/scripts/nginx/setup.php
new file mode 100755
index 000000000..7cb8592ae
--- /dev/null
+++ b/www/nginx/src/opnsense/scripts/nginx/setup.php
@@ -0,0 +1,105 @@
+#!/usr/local/bin/php
+userlist->__items as $user_list) {
+ $attributes = $user_list->getAttributes();
+ $uuid = $attributes['uuid'];
+ $file = null;
+ try {
+ $file = fopen("/var/db/nginx/auth/" . $uuid, "wb");
+ $users = explode(',',(string)$user_list->users);
+ foreach ($users as $user) {
+ $user_node = $nginx->getNodeByReference("credential." . $user);
+ $username = (string)$user_node->username;
+ $password = crypt((string)$user_node->password);
+ fwrite($file, $username . ':' . $password . "\n");
+ }
+ }
+ finally {
+ if (isset($file)) {
+ fclose($file);
+ }
+ unset($file);
+ }
+}
diff --git a/www/nginx/src/opnsense/service/conf/actions.d/actions_nginx.conf b/www/nginx/src/opnsense/service/conf/actions.d/actions_nginx.conf
new file mode 100644
index 000000000..afe983676
--- /dev/null
+++ b/www/nginx/src/opnsense/service/conf/actions.d/actions_nginx.conf
@@ -0,0 +1,27 @@
+[start]
+command:/usr/local/opnsense/scripts/nginx/setup.php;/usr/local/etc/rc.d/php-fpm start;/usr/local/etc/rc.d/nginx start
+parameters:
+type:script
+message:starting nginx
+
+[stop]
+command:/usr/local/etc/rc.d/nginx stop
+parameters:
+type:script
+message:stopping nginx
+
+[restart]
+command:/usr/local/opnsense/scripts/nginx/setup.php;/usr/local/etc/rc.d/php-fpm restart;/usr/local/etc/rc.d/nginx restart
+parameters:
+type:script
+message:restarting nginx
+
+[status]
+command:/usr/local/etc/rc.d/nginx status;exit 0
+parameters:
+type:script_output
+
+[phpstatus]
+command:/usr/local/etc/rc.d/php-fpm status; exit 0
+parameters:
+type:script_output
diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/+TARGETS b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/+TARGETS
new file mode 100644
index 000000000..320f535db
--- /dev/null
+++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/+TARGETS
@@ -0,0 +1,7 @@
+nginx:/etc/rc.conf.d/nginx
+nginx.conf:/usr/local/etc/nginx/nginx.conf
+webgui.conf:/usr/local/etc/nginx/nginx_web.conf
+mime.types:/usr/local/etc/nginx/mime.types
+php_fpm:/etc/rc.conf.d/php_fpm
+php-www.conf:/usr/local/etc/php-fpm.d/www.conf
+php-webgui.conf:/usr/local/etc/php-fpm.d/webgui.conf
diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/http.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/http.conf
new file mode 100644
index 000000000..7cf830c59
--- /dev/null
+++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/http.conf
@@ -0,0 +1,177 @@
+include mime.types;
+
+{% include "OPNsense/Nginx/ruleset.conf" ignore missing with context %}
+
+
+log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+log_format anonymized ':: - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+
+#tcp_nopush on;
+
+# 200M should be big enough for file servers etc.
+client_max_body_size 200M;
+brotli_static on;
+brotli on;
+gzip_static on;
+gzip on;
+server_tokens off;
+sendfile {% if OPNsense.Nginx.http.sendfile is defined and OPNsense.Nginx.http.sendfile == '1' %}On{% else %}Off{% endif %};
+{% if OPNsense.Nginx.http.default_type is defined and OPNsense.Nginx.http.default_type != '' %}
+default_type {{ OPNsense.Nginx.http.default_type }};
+{% else %}
+default_type application/octet-stream;
+{% endif %}
+{% if OPNsense.Nginx.http.keepalive_timeout is defined and OPNsense.Nginx.http.keepalive_timeout != '' %}
+keepalive_timeout {{ OPNsense.Nginx.http.keepalive_timeout }};
+{% endif %}
+
+#add_header X-Frame-Options SAMEORIGIN;
+#add_header X-Content-Type-Options nosniff;
+#add_header X-XSS-Protection "1; mode=block";
+#add_header Referrer-Policy "same-origin";
+
+# TODO add when core is ready for allowing nginx to serve the web interface
+# include nginx_web.conf;
+
+{% include "OPNsense/Nginx/upstream.conf" ignore missing with context %}
+
+{% set listen_list = [] %}
+{% for server in helpers.toList('OPNsense.Nginx.http_server') %}
+{% set single_servername = server.servername.split(",")[0] %}
+server {
+{% if server.listen_http_port is defined %}
+ listen [::]:{{ server.listen_http_port }}{% if server.listen_https_port not in listen_list%} ipv6only=off{% endif %};
+{% do listen_list.append(server.listen_http_port) %}
+{% endif %}
+{% if server.listen_https_port is defined and server.certificate is defined %}
+ listen [::]:{{ server.listen_https_port }}{% if server.listen_https_port not in listen_list%} ipv6only=off{% endif %} http2 ssl;
+{% do listen_list.append(server.listen_https_port) %}
+{% if server.ca is defined %}
+ ssl_client_certificate /usr/local/etc/nginx/key/{{ single_servername }}_ca.pem;
+ ssl_verify_client {{ server.verify_client }};
+{% endif %}
+ ssl_certificate_key /usr/local/etc/nginx/key/{{ single_servername }}.key;
+ ssl_certificate /usr/local/etc/nginx/key/{{ single_servername }}.pem;
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_dhparam /usr/local/etc/dh-parameters.4096;
+ ssl_ciphers 'ECDHE-ECDSA-CAMELLIA256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CAMELLIA256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CAMELLIA128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CAMELLIA128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-CAMELLIA256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-CAMELLIA256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-CAMELLIA128-SHA256:ECDHE-RSA-AES128-SHA256';
+ ssl_session_timeout 1d;
+ ssl_session_cache shared:SSL:50m;
+ ssl_session_tickets off;
+ ssl_prefer_server_ciphers on;
+ add_header Strict-Transport-Security max-age=15768000;
+ sendfile {% if server.sendfile is defined and server.sendfile == '1' %}On{% else %}Off{% endif %};
+ proxy_set_header X-TLS-Cipher $ssl_cipher;
+ proxy_set_header X-TLS-Protocol $ssl_protocol;
+ proxy_set_header X-TLS-SNI-Host $ssl_server_name;
+{% endif %}
+ # proxy headers for backend server
+{% if server.verify_client != 'off' %}
+ proxy_set_header X-Client-Dn $ssl_client_s_dn;
+ proxy_set_header X-Client-Verify $ssl_client_verify;
+{% endif %}
+{% if server.verify_client == 'optional_no_ca' %}
+ proxy_set_header X-Client-Certificate $ssl_client_escaped_cert;
+{% endif %}
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ server_name {{ server.servername }};
+ charset {{ server.charset }};
+ access_log /var/log/nginx/{{ server.servername }}.access.log {{ server.access_log_format }};
+ #include tls.conf;
+ error_page 404 /opnsense_error_404.html;
+ error_page 500 501 502 503 504 /opnsense_server_error.html;
+ # 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 %};
+ location @permanentban {
+ access_log /var/log/nginx/permanentban.access.log main;
+ internal;
+ add_header Content-Type text/plain;
+ add_header Charset utf-8;
+ return 403 "You got banned permanently from this server.";
+ }
+ error_page 418 = @permanentban;
+ location /opnsense_server_error.html {
+ internal;
+ root /usr/local/etc/nginx/views;
+ }
+ location /opnsense_error_404.html {
+ internal;
+ root /usr/local/etc/nginx/views;
+ }
+ location /waf_denied.html {
+ root /usr/local/etc/nginx/views;
+ access_log /var/log/nginx/waf_denied.access.log main;
+ }
+{% if server.enable_acme_support is defined and server.enable_acme_support == '1' %}
+ location ^~ /.well-known/acme-challenge/ {
+ default_type "text/plain";
+ root /var/etc/acme-client/challenges;
+ }
+{% endif %}
+ # block based on User Agents - stuff I have found over the years in my server log
+ if ($http_user_agent ~* Python-urllib|Nmap|python-requests|libwww-perl|MJ12bot|Jorgee|fasthttp|libwww|Telesphoreo|A6-Indexer|ltx71|okhttp|ZmEu) {
+ return 418;
+ }
+ if ($http_user_agent ~ "Indy\sLibrary|Morfeus Fucking Scanner")
+ {
+ return 418;
+ }
+ location /opnsense-auth-request {
+ internal;
+ fastcgi_pass unix:/var/run/php-webgui.socket;
+ fastcgi_index index.php;
+ fastcgi_param TLS-Cipher $ssl_cipher;
+ fastcgi_param TLS-Protocol $ssl_protocol;
+ fastcgi_param TLS-SNI-Host $ssl_server_name;
+ fastcgi_param Original-URI $request_uri;
+ fastcgi_param Original-HOST $host;
+{% if helpers._template_in_data['__uuid__'] is defined %}
+{% for uuid in helpers._template_in_data['__uuid__'] %}
+{% if helpers._template_in_data['__uuid__'][uuid] == server %}
+ fastcgi_param SERVER-UUID "{{ uuid }}";
+{% endif %}
+{% endfor %}
+{% endif %}
+ fastcgi_param SCRIPT_FILENAME /usr/local/opnsense/scripts/nginx/ngx_auth.php;
+ fastcgi_intercept_errors on;
+ include fastcgi_params;
+ }
+{% if server.block_nonpublic_data is defined and server.block_nonpublic_data == '1' %}
+ # apache htpasswd and htaccess
+ location ~ /\.ht {
+ return 403;
+ }
+ # those files may expose file system stuff
+ location ~ \.DS_Store$ {
+ return 403;
+ }
+{% endif %}
+{% if server.https_only is defined and server.https_only == '1' %}
+ if ($scheme != "https") {
+ return 302 https://$host$request_uri;
+ }
+{% endif %}
+{% if server.rewrites is defined %}
+{% for rewrite_uuid in server.rewrites.split(',') %}
+{% set rewrite = helpers.getUUID(rewrite_uuid) %}
+ rewrite {{ rewrite.source }} {{ rewrite.destination }}{% if rewrite.flag is defined%} {{ rewrite.flag }}{% endif %};
+{% endfor %}
+{% endif %}
+
+{% if server.locations is defined %}
+{% for location_uuid in server.locations.split(',') %}
+{% set location = helpers.getUUID(location_uuid) %}
+{% include "OPNsense/Nginx/location.conf" ignore missing with context %}
+{% endfor %}
+{% endif %}
+
+}
+
+{% endfor %}
diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/location.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/location.conf
new file mode 100644
index 000000000..80401f2c7
--- /dev/null
+++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/location.conf
@@ -0,0 +1,62 @@
+
+location {{ location.matchtype }} {{ location.urlpattern }} {
+{% if location.enable_secrules is defined and location.enable_secrules == '1' %}
+ SecRulesEnabled;
+{% endif %}
+{% if location.enable_learning_mode is defined and location.enable_learning_mode == '1' %}
+ LearningMode;
+{% endif %}
+{% if location.xss_block_score is defined %}
+ LibInjectionXss;
+ CheckRule "$LIBINJECTION_XSS >= {{ location.xss_block_score }}" BLOCK;
+{% endif %}
+{% set added_policies = [] %}
+{% if location.custom_policy is defined %}
+{% for custom_policy_uuid in location.custom_policy.split(',') %}
+{% set custom_policy = helpers.getUUID(custom_policy_uuid) %}
+{% set naxsi_ruletype = 'basic' %}
+{% include "OPNsense/Nginx/naxsirule.conf" ignore missing with context %}
+ CheckRule "$policy{{ custom_policy_uuid.replace('-', '') }} {{ custom_policy.operator }} {{ custom_policy.value
+ }}" {{ custom_policy.action }};
+{% endfor %}
+{% endif %}
+{% if location.rewrites is defined %}
+{% for rewrite_uuid in location.rewrites.split(',') %}
+{% set rewrite = helpers.getUUID(rewrite_uuid) %}
+ rewrite {{ rewrite.source }} {{ rewrite.destination }}{% if rewrite.flag is defined%} {{ rewrite.flag }}{% endif %};
+{% endfor %}
+{% endif %}
+{% if location.sqli_block_score is defined %}
+ LibInjectionSql;
+ CheckRule "$LIBINJECTION_SQL >= {{ location.sqli_block_score }}" BLOCK;
+{% endif %}
+ DeniedUrl "/waf_denied.html";
+{% if location.force_https is defined and location.force_https == '1' %}
+ if ($scheme != "https") {
+ return 302 https://$host$request_uri;
+ }
+{% endif %}
+{% if location.root is defined %}
+ root {{ location.root }};
+{% endif %}
+{% if location.index is defined %}
+ index {{ location.index.replace(",", " ") }};
+{% endif %}
+{% if location.autoindex is defined and location.autoindex == '1' %}
+ autoindex on;
+{% else %}
+ autoindex off;
+{% 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 }};
+{% else %}
+{% if location.advanced_acl is defined and location.advanced_acl == '1' %}
+ auth_request /opnsense-auth-request;
+{% endif %}
+{% endif %}
+{% if location.upstream is defined %}
+ proxy_pass http://upstream{{ location.upstream.replace('-','') }};
+{% endif %}
+
+}
diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/mime.types b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/mime.types
new file mode 100644
index 000000000..bf1b4c4f9
--- /dev/null
+++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/mime.types
@@ -0,0 +1,100 @@
+types {
+ text/html html htm shtml;
+ text/css css;
+ text/xml xml;
+ image/gif gif;
+ image/jpeg jpeg jpg;
+ application/javascript js;
+ application/atom+xml atom;
+ application/rss+xml rss;
+
+ text/mathml mml;
+ text/plain txt;
+ text/vnd.sun.j2me.app-descriptor jad;
+ text/vnd.wap.wml wml;
+ text/x-component htc;
+
+ image/png png;
+ image/svg+xml svg svgz;
+ image/tiff tif tiff;
+ image/vnd.wap.wbmp wbmp;
+ image/webp webp;
+ image/x-icon ico;
+ image/x-jng jng;
+ image/x-ms-bmp bmp;
+
+ application/font-woff woff;
+ application/java-archive jar war ear;
+ application/json json;
+ application/mac-binhex40 hqx;
+ application/msword doc;
+ application/pdf pdf;
+ application/postscript ps eps ai;
+ application/rtf rtf;
+ application/vnd.apple.mpegurl m3u8;
+ application/vnd.google-earth.kml+xml kml;
+ application/vnd.google-earth.kmz kmz;
+ application/vnd.ms-excel xls;
+ application/vnd.ms-fontobject eot;
+ application/vnd.ms-powerpoint ppt;
+ application/vnd.oasis.opendocument.graphics odg;
+ application/vnd.oasis.opendocument.presentation odp;
+ application/vnd.oasis.opendocument.spreadsheet ods;
+ application/vnd.oasis.opendocument.text odt;
+ application/vnd.openxmlformats-officedocument.presentationml.presentation
+ pptx;
+ application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
+ xlsx;
+ application/vnd.openxmlformats-officedocument.wordprocessingml.document
+ docx;
+ application/vnd.wap.wmlc wmlc;
+ application/x-7z-compressed 7z;
+ application/x-cocoa cco;
+ application/x-java-archive-diff jardiff;
+ application/x-java-jnlp-file jnlp;
+ application/x-makeself run;
+ application/x-perl pl pm;
+ application/x-pilot prc pdb;
+ application/x-rar-compressed rar;
+ application/x-redhat-package-manager rpm;
+ application/x-sea sea;
+ application/x-shockwave-flash swf;
+ application/x-stuffit sit;
+ application/x-tcl tcl tk;
+ application/x-x509-ca-cert der pem crt;
+ application/x-xpinstall xpi;
+ application/xhtml+xml xhtml;
+ application/xspf+xml xspf;
+ application/zip zip;
+ application/gzip gz;
+ application/xz xz;
+
+ application/octet-stream bin exe dll;
+ application/octet-stream deb;
+ application/octet-stream dmg;
+ application/octet-stream iso img;
+ application/octet-stream msi msp msm;
+
+ audio/midi mid midi kar;
+ audio/mpeg mp3;
+ audio/ogg ogg oga;
+ audio/opus opus
+ audio/speex spx;
+ audio/x-m4a m4a;
+ audio/x-realaudio ra;
+ audio/flac flac;
+
+ video/3gpp 3gpp 3gp;
+ video/mp2t ts;
+ video/mp4 mp4;
+ video/mpeg mpeg mpg;
+ video/quicktime mov;
+ video/webm webm;
+ video/ogg ogv;
+ video/x-flv flv;
+ video/x-m4v m4v;
+ video/x-mng mng;
+ video/x-ms-asf asx asf;
+ video/x-ms-wmv wmv;
+ video/x-msvideo avi;
+}
diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/naxsirule.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/naxsirule.conf
new file mode 100644
index 000000000..bc543f3fa
--- /dev/null
+++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/naxsirule.conf
@@ -0,0 +1,62 @@
+{% macro naxsi_mzhelper(mz_helper_rule) -%}
+{% set mz_matches = [] %}
+{% set rx_suffix = '' %}
+{% if mz_helper_rule.regex is defined and mz_helper_rule.regex == '1' %}
+{% set rx_suffix = '_X' %}
+{% endif %}
+{% if mz_helper_rule.args == '1' %}
+{% do mz_matches.append('ARGS') %}
+{% endif %}
+{% if mz_helper_rule.headers == '1' %}
+{% do mz_matches.append('HEADERS') %}
+{% endif %}
+{% if mz_helper_rule.name == '1' %}
+{% if mz_matches|length > 0 %}
+{% do mz_matches.append('NAME') %}
+{% endif %}
+{% endif %}
+{% if mz_helper_rule.raw_body == '1' %}
+{% do mz_matches.append('RAW_BODY') %}
+{% endif %}
+{% if mz_helper_rule.file_extension == '1' %}
+{% do mz_matches.append('FILE_EXT') %}
+{% endif %}
+{% if mz_helper_rule.dollar_body_var is defined and mz_helper_rule.dollar_body_var != '' %}
+{% do mz_matches.append('$BODY_VAR' + rx_suffix + ':' + mz_helper_rule.dollar_body_var) %}
+{% endif %}
+{% if mz_helper_rule.dollar_args_var is defined and mz_helper_rule.dollar_args_var != '' %}
+{# in case of regex, we cannot use args_var -> https://github.com/nbs-system/naxsi/wiki/matchzones-bnf#match-zone #}
+{% if mz_helper_rule.url is defined and mz_helper_rule.url != '' %}
+{% do mz_matches.append('$ARGS_VAR' + rx_suffix + ':' + mz_helper_rule.dollar_args_var) %}
+{% endif %}
+{% endif %}
+{% if mz_helper_rule.dollar_headers_var is defined and mz_helper_rule.dollar_headers_var != '' %}
+{% do mz_matches.append('$HEADERS_VAR' + rx_suffix +':' + mz_helper_rule.dollar_headers_var) %}
+{% endif %}
+{% if mz_helper_rule.dollar_url is defined and mz_helper_rule.dollar_url != '' %}
+{% do mz_matches.insert(0,'$URL' + rx_suffix +':' + mz_helper_rule.dollar_url) %}
+{% endif %}
+{{ mz_matches|join('|') }}
+{%- endmacro %}
+{% macro naxsi_rule(uuid, rule, ruletype) -%}
+ {{ ruletype }}{% if rule.negate is defined and rule.negate == '1' %} negative{% endif
+ %} {{ rule.match_type }}:{{ rule.identifier }} "{% if rule.regex == '1' %}rx{% else %}str{% endif
+ %}:{{ rule.match_value }}" "msg:{{ rule.message }}" "mz:{{ naxsi_mzhelper(rule)
+ }}" "s:$policy{{ uuid.replace('-', '') }}:{{ rule.score }}";
+{%- endmacro %}
+
+{% if naxsi_ruletype == 'basic' %}
+{# current policy in loop is available as custom_policy, the uuid as custom_policy_uuid #}
+{% for naxsi_rule_uuid in custom_policy.naxsi_rules.split(',') %}
+{% if naxsi_rule_uuid not in added_policies %}
+{% set basic_rule = helpers.getUUID(naxsi_rule_uuid) %}
+{% if basic_rule.ruletype == 'basic' %}
+{{ naxsi_rule(custom_policy_uuid, basic_rule, "BasicRule") }}
+{% do added_policies.append(naxsi_rule_uuid) %}
+{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if naxsi_ruletype == 'main' %}
+{{ naxsi_rule(custom_policy_uuid, main_rule, "MainRule") }}
+{% endif %}
diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/nginx b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/nginx
new file mode 100644
index 000000000..598e6444f
--- /dev/null
+++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/nginx
@@ -0,0 +1,2 @@
+nginx_enable="YES"
+nginx_var_script="/usr/local/opnsense/scripts/nginx/setup.php"
diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/nginx.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/nginx.conf
new file mode 100644
index 000000000..480fe8fd6
--- /dev/null
+++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/nginx.conf
@@ -0,0 +1,29 @@
+{# load naxsi WAF module #}
+load_module /usr/local/libexec/nginx/ngx_stream_module.so;
+load_module /usr/local/libexec/nginx/ngx_http_naxsi_module.so;
+load_module /usr/local/libexec/nginx/ngx_mail_module.so;
+load_module /usr/local/libexec/nginx/ngx_http_brotli_filter_module.so;
+load_module /usr/local/libexec/nginx/ngx_http_brotli_static_module.so;
+
+# TODO enable root when running the web interface
+#user root wheel;
+worker_processes 1;
+
+error_log /var/log/nginx/error.log;
+
+events {
+ worker_connections 1024;
+}
+
+http {
+{% if helpers.exists('OPNsense.Nginx') %}
+{# include http blocks partial #}
+{% include "OPNsense/Nginx/http.conf" ignore missing with context %}
+{% endif %}
+}
+{% if helpers.exists('OPNsense.Nginx') %}
+# mail {
+{# include http blocks partial #}
+{% include "OPNsense/Nginx/mail.conf" ignore missing with context %}
+# }
+{% endif %}
diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/php-webgui.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/php-webgui.conf
new file mode 100644
index 000000000..3ed5154ac
--- /dev/null
+++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/php-webgui.conf
@@ -0,0 +1,16 @@
+{% raw %}
+[webgui]
+user = root
+group = wheel
+listen = /var/run/php-webgui.socket
+listen.owner = root
+listen.group = wheel
+listen.mode = 0660
+pm = dynamic
+pm.max_children = 5
+pm.start_servers = 2
+pm.min_spare_servers = 1
+pm.max_spare_servers = 3
+php_admin_value[error_log] = /var/log/fpm-php.www.log
+php_admin_flag[log_errors] = on
+{% endraw %}
diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/php-www.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/php-www.conf
new file mode 100644
index 000000000..30547aacd
--- /dev/null
+++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/php-www.conf
@@ -0,0 +1,11 @@
+{% raw %}
+[www]
+user = www
+group = www
+listen = /var/run/php-www.socket
+pm = dynamic
+pm.max_children = 5
+pm.start_servers = 2
+pm.min_spare_servers = 1
+pm.max_spare_servers = 3
+{% endraw %}
diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/php_fpm b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/php_fpm
new file mode 100644
index 000000000..1f644f9c3
--- /dev/null
+++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/php_fpm
@@ -0,0 +1,2 @@
+php_fpm_enable="YES"
+command_args="-R"
diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/ruleset.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/ruleset.conf
new file mode 100644
index 000000000..d08db7a21
--- /dev/null
+++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/ruleset.conf
@@ -0,0 +1,29 @@
+{% set naxsi_ruletype = 'main' %}
+{% set main_policies = [] %}
+{% set main_rules = [] %}
+{# collect custom policy UUIDs from locations so we know, which ones are probably in use #}
+{% if OPNsense.Nginx.location is defined %}
+{% for location in helpers.toList('OPNsense.Nginx.location') %}
+{% if location.custom_policy is defined and location.custom_policy != ""%}
+{% for custompolicy_uuid in location.custom_policy.split(',') %}
+{% if not custompolicy_uuid in main_policies %}
+{% do main_policies.append(custompolicy_uuid) %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% for custom_policy_uuid in main_policies %}
+{% set custom_policy = helpers.getUUID(custom_policy_uuid) %}
+{% if custom_policy.naxsi_rules is defined %}
+{% for main_rule_uuid in custom_policy.naxsi_rules.split(',') %}
+{% if main_rule_uuid not in main_rules %}
+{% do main_rules.append(main_rule_uuid) %}
+{% set main_rule = helpers.getUUID(main_rule_uuid) %}
+{% if main_rule.ruletype == 'main' %}
+{% include "OPNsense/Nginx/naxsirule.conf" ignore missing with context %}
+{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/upstream.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/upstream.conf
new file mode 100644
index 000000000..d1fb7db35
--- /dev/null
+++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/upstream.conf
@@ -0,0 +1,22 @@
+# UPSTREAM SERVERS
+{% set upstreamlist = {} %}
+{% for location in helpers.toList('OPNsense.Nginx.location') %}
+{% if location.upstream is defined %}
+{% do upstreamlist.update({location.upstream: helpers.getUUID(location.upstream)}) %}
+{% endif %}
+{% endfor %}
+{% for upstream_uuid, upstream in upstreamlist.items() %}
+{% for upstream_serveruuid in upstream.serverentries.split(',') %}
+upstream upstream{{ upstream_uuid.replace('-','') }} {
+{% set upstream_server = helpers.getUUID(upstream_serveruuid) %}
+server {% if ':' in upstream_server.server %}[{% endif %}{{ upstream_server.server }}{% if ':' in upstream_server.server %}]{% endif
+ %}{% if upstream_server.port is defined %}:{{ upstream_server.port }}{% endif
+ %}{% if upstream_server.priority is defined %} weight={{ upstream_server.priority }}{% endif
+ %}{% if upstream_server.max_conns is defined %} max_conns={{ upstream_server.max_conns }}{% endif
+ %}{% if upstream_server.max_fails is defined %} max_fails={{ upstream_server.max_fails }}{% endif
+ %}{% if upstream_server.fail_timeout is defined %} fail_timeout={{ upstream_server.fail_timeout }}{% endif
+ %}{% if upstream_server.no_use is defined %} {{ upstream_server.no_use }}{% endif %};
+{% endfor %}
+
+}
+{% endfor %}
diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/webgui.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/webgui.conf
new file mode 100644
index 000000000..dd6649bf6
--- /dev/null
+++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/webgui.conf
@@ -0,0 +1,205 @@
+server {
+
+ keepalive_requests 15;
+ keepalive_timeout 30;
+
+ root /usr/local/www/;
+{% if system.webgui.protocol is defined and system.webgui.protocol == 'https' %}
+ if ($scheme != "https") {
+ return 302 https://$host$request_uri;
+ }
+ listen 80 default_server; # if redirect is enabled
+ listen {% if system.webgui.port is defined and system.webgui.port != '' %}{{ system.webgui.port }}{% else %}443{% endif %} ssl http2 default_server;
+ ## TLS configuration
+ ssl_dhparam /usr/local/etc/dh-parameters.4096;
+ ssl_ecdh_curve secp384r1;
+ ssl_certificate /var/etc/cert.pem;
+ ssl_certificate_key /var/etc/cert.pem;
+ ssl_client_certificate /var/etc/ca.pem;
+ ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
+ {% if system.webgui['ssl-ciphers'] is defined and system.webgui['ssl-ciphers'] != '' %}
+ ssl_ciphers {{ system.webgui['ssl-ciphers'] }}
+ {% else %}
+ ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
+ {% endif %}
+{% else %}
+ listen {% if system.webgui.port is defined and system.webgui.port != '' %}{{ system.webgui.port }}{% else %}80{% endif %};
+{% endif %}
+
+ autoindex off;
+
+ # gzip compression
+ gzip_static on;
+{% if system.webgui.compression is defined and system.webgui.compression != '' %}
+ gzip on;
+ gzip_comp_level {{ system.webgui.compression }};
+{% else %}
+ gzip off;
+{% endif %}
+ #compress.cache-dir = "/tmp/lighttpdcompress/"
+ gzip_types text/plain text/css text/xml text/javascript;
+
+ #server.upload-dirs = ( "/root/", "/tmp/", "/var/" )
+ # server.max-request-size = 2097152
+
+ expires 50h;
+
+ # Maximum idle time with nothing being written (php downloading)
+ #fastcgi_read_timeout = 999
+
+ ## where to send error/access-messages to
+ access_log syslog:server=127.0.0.1,facility=daemon;
+ access_log /var/log/nginx/webgui.access.log;
+ error_log syslog:server=127.0.0.1,facility=daemon;
+ error_log /var/log/nginx/webgui.error.log debug;
+
+ index index.php index.html index.htm default.htm;
+
+ # mimetype mapping
+ types {
+ application/x-ns-proxy-autoconfig pad.dat;
+ application/pdf pdf;
+ application/pgp-signature sig;
+ application/futuresplash spl;
+ application/octet-stream class;
+ application/postscript ps;
+ application/x-bittorrent torrent;
+ application/x-dvi dvi;
+ application/x-gzip gz;
+ application/x-ns-proxy-autoconfig pac;
+ application/x-shockwave-flash swf;
+ application/x-tgz tar.gz tgz;
+ application/x-tar tar;
+ application/zip zip;
+ audio/mpeg mp3;
+ audio/x-mpegurl m3u;
+ audio/x-ms-wma wma;
+ audio/x-ms-wax wax;
+ audio/x-wav ogg;
+ audio/x-wav wav;
+ image/gif gif;
+ image/jpeg jpg jpeg;
+ image/png png;
+ image/svg+xml svg;
+ image/x-xbitmap xbm;
+ image/x-xpixmap xpm;
+ image/x-xwindowdump xwd;
+ text/css css;
+ text/html html htm;
+ text/javascript js;
+ text/plain asc;
+ text/plain c;
+ text/plain conf;
+ text/plain text txt;
+ text/xml dtd;
+ text/xml xml;
+ video/mpeg mpeg;
+ video/mpeg mpg;
+ video/quicktime mov qt;
+ video/x-msvideo avi;
+ video/x-ms-asf asf asx;
+ video/x-ms-wmv wmv;
+ application/x-bzip bz2;
+ application/x-bzip-compressed-tar tbz tar.bz2;
+ }
+
+ # Use the "Content-Type" extended attribute to obtain mime type if possible
+ #mimetypes.use-xattr = "enable"
+
+ ## deny access the file-extensions
+ #
+ # ~ is for backupfiles from vi, emacs, joe, ...
+ # .inc is often used for code includes which should in general not be part
+ # of the document-root
+ location ~* "(~|.inc)$" {
+ return 403;
+ }
+
+{% if helpers.exists('OPNsense.Nginx.webgui.limitnetworks') and OPNsense.Nginx.webgui.limitnetworks == '1' %}
+# whitelist only directly connected networks to prevent attacks over the internet to the web interface
+# we cannot block everything except RFC 1918 because this does not work with IPv6
+{% set whitelisted_networks = [] %}
+{% for interface_name in interfaces %}
+{% set interface = interfaces[interface_name] %}
+{% if interface.ipaddr is defined and interface.ipaddr != '' and '.' in interface.ipaddr %}
+{% if interface.subnet is defined and interface.subnet != '' %}
+{% set cidr = interface.ipaddr + '/' + interface.subnet %}
+{% else %}
+{% set cidr = interface.ipaddr %}
+{% endif %}
+{% if cidr not in whitelisted_networks %}
+{% do whitelisted_networks.append(cidr) %}
+ allow {{ cidr }};
+{% endif %}
+{% endif %}
+{% if interface.ipaddrv6 is defined and interface.ipaddrv6 != '' and ':' in interface.ipaddrv6 %}
+{% if interface.subnetv6 is defined and interface.subnetv6 != '' %}
+{% set cidr = interface.ipaddrv6 + '/' + interface.subnetv6 %}
+{% else %}
+{% set cidr = interface.ipaddrv6 %}
+{% endif %}
+{% if cidr not in whitelisted_networks %}
+{% do whitelisted_networks.append(cidr) %}
+ allow {{ cidr }};
+{% endif %}
+{% endif %}
+{% endfor %}
+{% if helpers.exists('virtualip') %}
+{% for intf_item in helpers.toList('virtualip.vip') %}
+{% if intf_item.type == 'single' %}
+{% set cidr = intf_item.subnet + '/' + intf_item.subnet_bits %}
+{% if cidr not in whitelisted_networks %}
+ allow {{ cidr }};
+{% do whitelisted_networks.append(cidr) %}
+{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+ deny all;
+{% endif %}
+
+ location ~ \.php$ {
+ fastcgi_pass unix:/var/run/php-webgui.socket;
+ fastcgi_split_path_info ^(.+\.php)(/.+)$;
+ fastcgi_index index.php;
+ fastcgi_param TLS-Cipher $ssl_cipher;
+ fastcgi_param TLS-Protocol $ssl_protocol;
+ fastcgi_param TLS-SNI-Host $ssl_server_name;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ fastcgi_intercept_errors on;
+ include fastcgi_params;
+ }
+
+ # Phalcon ui and api routing
+
+ location @apirequest {
+ root /usr/local/opnsense/www;
+ include fastcgi_params;
+ fastcgi_param QUERY_STRING &$query_string;
+ fastcgi_param SCRIPT_FILENAME /usr/local/opnsense/www/api.php;
+ fastcgi_param TLS-Cipher $ssl_cipher;
+ fastcgi_param TLS-Protocol $ssl_protocol;
+ fastcgi_param TLS-SNI-Host $ssl_server_name;
+ fastcgi_intercept_errors off;
+ fastcgi_pass unix:/var/run/php-webgui.socket;
+ }
+ location @guirequest {
+ root /usr/local/opnsense/www;
+ include fastcgi_params;
+ fastcgi_param QUERY_STRING $query_string;
+ fastcgi_param SCRIPT_FILENAME /usr/local/opnsense/www/index.php;
+ fastcgi_param TLS-Cipher $ssl_cipher;
+ fastcgi_param TLS-Protocol $ssl_protocol;
+ fastcgi_param TLS-SNI-Host $ssl_server_name;
+ fastcgi_intercept_errors off;
+ fastcgi_pass unix:/var/run/php-webgui.socket;
+ }
+ location ~ ^/ui/(?[^\?]+)(?\?(.*))? {
+ root /usr/local/opnsense/www;
+ try_files /$path @guirequest;
+ }
+ location ~ ^/api/(?[^\?]+)(?\?(.*))?{
+ root /usr/local/opnsense/www;
+ try_files /$path @apirequest;
+ }
+}