net/sslh: Initial plugin version (#2729)

This is to address the discussion about a plugin for sslh and resolves #1630.

* Includes setting listen addresses, and protocol targets.
* Includes some other advanced settings
* Service start/stop/restart control
This commit is contained in:
agh1467
2022-10-18 13:59:15 +02:00
committed by GitHub
parent ea4fdeb57e
commit 76dc5e9b59
19 changed files with 1082 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
# 1.21c_1
* Initial Version
* Includes setting listen addresses, and protocol targets.
* Includes some other advanced settings
* Service start/stop/restart control
+171
View File
@@ -0,0 +1,171 @@
# Development Notes
The initial version of this plugin is going to cover only the most basic of settings. A first pass will include primarily the settings which are available through the command line, and included in documentation.
## Configuration
For configuration, this plugin utilizes the configuration file to set settings. It has more options, and parity for all settings at the command line that are needed to be used in OPNsense. I didn't want to go as far as manipulating the startup script's command execution to inject command line options.
Most settings are available through the configuration file. A sample is provided through the FreeBSD ports installation:
/usr/local/etc/sslh.conf.sample:
```
# This is a basic configuration file that should provide
# sensible values for "standard" setup.
verbose: 0;
foreground: false;
inetd: false;
numeric: false;
transparent: false;
timeout: 2;
user: "nobody";
pidfile: "/var/run/sslh.pid";
chroot: "/var/empty";
# Change hostname with your external address name.
listen:
(
{ host: "thelonious"; port: "443"; }
);
protocols:
(
{ name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; fork: true; },
{ name: "openvpn"; host: "localhost"; port: "1194"; },
{ name: "xmpp"; host: "localhost"; port: "5222"; },
{ name: "http"; host: "localhost"; port: "80"; },
{ name: "tls"; host: "localhost"; port: "443"; log_level: 0; },
{ name: "anyprot"; host: "localhost"; port: "443"; }
);
```
Here is a table which maps the configurations to command line options, and includes the model data types, default values, and if a hint is defined.
| Configuration Key | Command line Option | Model Field Type | Field Default | Hint |
| ------------------ | ------------------- | ---------------- | ------------- | ------------- |
| verbose | -v, --verbose | BooleanField | N/A | |
| foreground | -f, --foreground | N/A | N/A | |
| inetd | -i, --inetd | N/A | N/A | |
| numeric | -n, --numeric | BooleanField | N/A | 2 |
| timeout | -t, --timeout | IntegerField | N/A | |
| user | -u, --user | N/A | N/A | |
| pidfile | -P, --pidfile | N/A | N/A | |
| chroot | -C, --chroot | N/A | N/A | |
| on_timeout | --on-timeout | OptionField | ssh | |
| listen | -p, --listen | CSVListField | localhost:443 | |
| protocols | --ssl, --tls<br>--ssh<br>--openvpn<br>--http<br>--xmpp<br>--tinc<br>--anyprot | TextField | N/A | localhost:443<br>localhost:22<br>localhost:1194<br>localhost:80<br>localhost:5222<br>localhost:655 |
Here are some non-configuration options which have representation in the model:
| Model Node | Model Field Type | Field Default | Hint |
| ------------------ | ---------------- | ------------- | ------------- |
| mode | OptionField | fork | |
Most settings have defaults set within the application itself, so setting defaults in the model would be redundant. Hints were provided where this occurred, and it happened that the HTML element supports a hint. The only settings with field defaults are the dropdown `OptionFields` which are set to required because selecting nothing for these would require some additional code in the Jinja template to accommodate a blank value. Since they're required, setting a default is best so the user doesn't have to interact with them. Especially since these fields are advanced and would be hidden.
The configuration file has some functionality which isn't explained in the main documentation pages, but is included in some example configuration files (see `fork`, and `log_level` in sample above), and some in the source code. These settings are excluded for now, and need to be investigated further to see what OPNsense model data types would be best to use and how to visualize these settings in the UI.
Some advanced settings can be seen in the test configuration file in the source:
https://github.com/yrutschle/sslh/blob/master/test.cfg
There are also more settings for each protocol (maybe individually?), and it's also possible to define multiple entries for some (all?) settings:
```
protocols: (
{ name: "tls";
host: "localhost";
port: "993";
sni_hostnames: [ "mail.rutschle.net" ];
},
{ name: "tls";
host: "localhost";
port: "xmpp-client";
sni_hostnames: [ "im.rutschle.net" ];
},
{ name: "tls";
host: "localhost";
port: "4443";
sni_hostnames: [ "www.rutschle.net" ];
}
);
```
There is also a more advanced "regex" protocol which can be defined multiple times:
```
protocols: (
{ name: "regex";
host: "192.168.0.2";
port: "80";
regex_patterns:
["^(GET|POST|PUT|OPTIONS|DELETE|HEADER) [^ ]* HTTP/[0-9.]*[\r\n]*Host: host_A.acme"] },
{ name: "regex";
host: "192.168.0.3";
port: "80";
regex_patterns:
["^(GET|POST|PUT|OPTIONS|DELETE|HEADER) [^ ]* HTTP/[0-9.]*[\r\n]*Host: host_B.acme"] }
);
```
To support multiple entries, an `ArrayField` type will have to be used, and bootgrids utilized to control the entries within each field. For that it may be best to split out each protocol onto a separate tab, rather than have them displayed on the same tab (may look cluttered).
The style of the entries here is different than those provided in the sample which would require some additional care in the Jinja template if that style is to be used.
## Command Line Options
For reference, here are the command line options:
Command line options:
```
sslh
[-Fconfig file]
[-t num]
[--transparent]
[-p listening address [-p listening address ...]
[--ssl target address for SSL]
[--tls target address for TLS]
[--ssh target address for SSH]
[--openvpn target address for OpenVPN]
[--http target address for HTTP]
[--xmpp target address for XMPP]
[--tinc target address for TINC]
[--anyprot default target address] (use this for SSLv2 connections)
[--on-timeout protocol name]
[-u username]
[-C chroot] [-P pidfile] [-v] [-i] [-V] [-f] [-n]
```
## Logging
This application may not have a function to output to a log file. Documentation indicates that `sslh` should be started manually, and run with the `foreground` option to get log messages clearly. Otherwise, logs are sent to the `syslog` facility. There is a configuration setting: `syslog_facility: "auth";` which might be used to change this behavior.
On OPNsense 22.1 the logging goes to the `audit` facility, and looks something like this:
```
2021-12-29T23:41:07+00:00 OPNsense.localdomain sslh-fork[62839] 62839 - [meta sequenceId="43"] sslh-fork 1.21c started
```
It's a bit noisy with the "meta sequence" part, and the PID being displayed twice. Hopefully that will get cleaned up eventually. It should be possible to utilize the built-in log API to display these log entries, and utilize a hard coded filter to display only messages for `sslh`.
A more crude option is to set the `foreground` option in the configuration file, start `sslh` via configd, and redirect the output to a file located in `/var/log/`. `configd` might not do well with this since commands that it runs are expected to exit. Maybe the `&` operator can be used to background the process, and maybe `configd` could deal with that better. It would definitely need more testing to confidently use as a solution though.
## Jinja Templates
The protocols section can probably be reduced to a single for loop which iterates through each variable, and appends the line to the list. The extra settings described above would have to be taken into consideration if some settings only apply to specific protocols. The alternative multi-line style would also have to be considered to make sure everything looks nice in the file.
## Protocol Order
The `anyprot` documentation mentions that `sslh` will try protocols in the order specified (at the command line). This probably also means that it applies for the configuration file as well. Being able to change the order, will eventually be necessary for full functionality. As for a standard order, the `man` page command line reference, the order in which the commands are detailed, and the sample configuration files all use different ordering.
This could possibly be done with a single `ArrayField` containing all protocol entries. With the protocol name being one of the fields in each entry. That would probably be better than the multi-field approach as described earlier. This would put all of the entries in a single bootgrid which could be displayed on a single page. A number field (`AutoNumberField`?) could be added to indicate the appropriate order. Without a UI function to perform this ordering it could get a bit complicated for new users. It's definitely more complicated than just a list of static boxes. Changing the order is not a function of the bootgrid at the moment.
There is also the need to consider that some options may only be available/function for specific protocols. There may be the possibility to employ constraints/requirements for field usage if a specific field only works with a specific protocol, but it will have to be investigated further. This would tie in also to the Jinja template because the protocols section could use for loop as described above, but would definitely have to display all of the additional options for each protocol.
## Service Status
The fact that this service can run as different binaries means that under specific circumstances, sometimes the service status can return a status of "not running" even when the service is running. The UI won't offer to stop the service because it thinks it's not running. If the service is configured to run with the one variant, and then the configuration is changed to use the another variant without a service restart (though some error occurring), then the service status will be looking for the new variant when looking at the status, and it will say "not running," but the previous variant will still be running. If there are no errors with saving the configuration, and the service API restarts the service successfully after saving the configuration, then it should be relatively rare occurrence.
## Transparent mode
This function is available using the command line option "-t, --transparent" or through the configuration file using the "transparent" keyword. The documentation describes this as a "Linux only" feature, and the documentation demonstrates using this feature in conjunction with `iptables`. Since FreeBSD doesn't have `iptables` it's probably that this feature won't work. Since there is no provisions for using this feature on FreeBSD, it's been excluded from the this plugin.
+8
View File
@@ -0,0 +1,8 @@
PLUGIN_NAME= sslh
PLUGIN_VERSION= 0.1
PLUGIN_DEVEL= yes
PLUGIN_COMMENT= sslh configuration front-end
PLUGIN_DEPENDS= sslh
PLUGIN_MAINTAINER= agh1467@protonmail.com
.include "../../Mk/plugins.mk"
+69
View File
@@ -0,0 +1,69 @@
# sslh plugin for OPNsense
## Introduction
This is a plugin for OPNsense firewall which provides a front-end UI for managing `SSLH`.
This plugin is designed for v`1.21c`, but may function with later versions.
Here are some resources for `SSLH`
[SSLH Project Home Page](https://www.rutschle.net/tech/sslh/README.html)
[SSLH Project Source Repository](https://github.com/yrutschle/sslh)
[SSLH FAQ](https://www.rutschle.net/tech/sslh/doc/FAQ.html)
## Features
This plugin, currently support only basic features like:
* Setting multiple listen addresses
* Setting protocol targets
* Controlling some advanced settings
## Operation
The plugin can be managed from Services -> SSLH in the OPNsense UI.
To begin, enable SSLH, define at least one listen address, and define a desired target protocol.
### Settings
Most settings for `SSLH` are included here in the UI, but some were left out due to the advanced nature, or the need to investigate further to understand the best approach to bring them into the UI. Help for each setting is included in the UI, select the "i" button to the left of each setting to show the help text.
#### Listen Addresses
This is a list of ADDRESS:PORT combinations. The list is comma delimited, and supports both IPv4, and IPv6 addresses.
#### Protocol Targets
The protocol targets each support one ADDRESS:PORT combination.
### Advanced Settings
Several settings are hidden by default, select the "advanced mode" button in the top left to access these settings.
#### Mode
This is also called the sslh "variant", and makes the start up script execute a separate binary, `sslh-fork` or `sslh-select` depending on selection. Each behaves differently, and has different performance.
#### Timeout
This is a global timeout, and has a default value of 2 seconds.
#### On Timeout
This defines the protocol to which connections will be sent after the timeout period. The default is SSH.
#### Verbose
This will increase the verbosity of the log messages in `SSLH`.
#### Numeric
This will force no DNS lookup, and make the logs contain IP addresses instead of hostnames.
### License
[![License](https://img.shields.io/badge/License-BSD%202--Clause-orange.svg)](https://opensource.org/licenses/BSD-2-Clause)
+19
View File
@@ -0,0 +1,19 @@
Manage SSLH, the SSL/SHH multiplexer via the OPNsense web UI.
SSLH is a service which accepts HTTPS, SSH, OpenVPN, tinc and XMPP connections on the same port.
This makes it possible to connect to any of these servers on port 443 while
still serving HTTPS on that port.
SSLH Project Home Page: https://www.rutschle.net/tech/sslh
SSLH FAQ: https://www.rutschle.net/tech/sslh/doc/FAQ.html
Changelog
================
0.1
* Initial Version
* Includes setting listen addresses, and protocol targets.
* Includes some other advanced settings
* Service start/stop/restart control
@@ -0,0 +1,69 @@
<?php
/*
Copyright (C) 2022 agh1467 <agh1467@protonmail.com>
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.
*/
/**
* Function to register the plugin's service with OPNsense.
*
* This adds the service to the System: Diagnostics: Services page at
* http://<opnsense>/status_services.php
*
* The suffix "_services" allows this function to be picked up by
* etc/inc/plugins.inc:plugins_services() and be included in the
* $services array to be processed by status_services.php.
*
* @return array the array of attributes for this service
*/
function sslh_services()
{
// Create an array to be processed by www/status_services.php
$service = array();
// Load in our settings to get the enabled state of the plugin.
$settings = new \OPNsense\Sslh\Settings();
// Only show the plugin if it's enabled.
if (! ((string) $settings->enabled == '1')) {
// return empty array if not enabled
return $services;
}
$configd_name = 'sslh';
$service[] = array(
'name' => 'sslh', // Service column
'description' => gettext('SSLH'), // Description column
'configd' => array( // Status column
'restart' => array($configd_name . ' restart'),
'start' => array($configd_name . ' start'),
'stop' => array($configd_name . ' stop'),
),
'pidfile' => '/var/run/sslh.pid'
);
return $service;
}
@@ -0,0 +1,77 @@
<?php
/**
* Copyright (C) 2022 agh1467@protonmail.com
*
* 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\Sslh\Api;
use OPNsense\Base\ApiMutableServiceControllerBase;
use OPNsense\Sslh;
/**
* An ApiMutableServiceControllerBase based class which is used to control
* the sslh service.
*
* @package OPNsense\Sslh
*/
class ServiceController extends ApiMutableServiceControllerBase
{
/**
* Reference the model class, which is used to determine if this service is
* enabled (links the model to the service)
*
* @var string $internalServiceClass
*/
protected static $internalServiceClass = '\OPNsense\Sslh\Settings';
/**
* Before starting the service it will call configd to generate configuration
* data, in this case it would execute the equivalent of configctl template
* reload OPNsense/Sslh on the console
*
* @var string $internalServiceTemplate
*/
protected static $internalServiceTemplate = 'OPNsense/Sslh';
/**
* Which section of the model contains a boolean defining if the service is
* enabled (settings.enabled)
*
* @var string $internalServiceEnabled
*/
protected static $internalServiceEnabled = 'enabled';
/**
* Refers to the actions configuraiton file, where it can find
* start/stop/restart/status/reload actions:
* src/opnsense/service/actions.d/actions_sslh.conf
* @var string $internalServiceName
*/
protected static $internalServiceName = 'sslh';
}
@@ -0,0 +1,133 @@
<?php
/**
* Copyright (C) 2022 agh1467@protonmail.com
*
* 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\Sslh\Api;
use OPNsense\Base\ApiMutableModelControllerBase;
/**
* An ApiMutableModelControllerBase class used to perform settings related
* actions for this plugin.
*
* This API is accessible at the following URL endpoint:
*
* `/api/sslh/settings`
*
* Functions with a name ending in "Action" become API endpoints by extending
* `ApiMutableModelControllerBase`. That class creates the following encpoints:
* ```
* search
* get
* add
* del
* set
* toggle
* ```
*
* @package OPNsense\Sslh
*/
class SettingsController extends ApiMutableModelControllerBase
{
/**
* This variable defines what to call the <model> that is defined for this
* Class by Phalcon. That is to say the model XML that has the same name as
* this controller's name, "Settings".
* In this case, it is the model XML file:
*
* `model/OPNsense/Sslh/Settings.xml`
*
* The model name is then used as the name of the array returned by setBase()
* and getBase(). In the form XMLs, the prefix used on the field IDs must
* match this name as API actions use the same name in their transactions.
* For example, the key_name in an API JSON response, will be this model
* name. This name is also used as the API endpoint for this Controller.
*
* `/api/sslh/settings`
*
* This locks activies of this Class to this specific model, so it won't
* save to other models, even within the same plugin.
*
* @var string $internalModelName
*/
protected static $internalModelName = 'settings';
/**
* Base model class to reference.
*
* This variable defines which class to call for getMode(). It is used in a
* ReflectionClass call to establish the model object. This class is defined
* in the models directory alongside the model XML, and has the same name
* as this Controller. This class extends BaseModel which reads the model
* XML that has the same name as the class.
*
* In this case, these are the model XML file, and class definition file:
*
* `model/OPNsense/Sslh/Settings.xml`
*
* `model/OPNsense/Sslh/Settings.php`
*
* These together will establish several API endpoints on this Controller's
* endpoint including:
*
* `/api/sslh/settings/get`
*
* `/api/sslh/settings/set`
*
* These are both defined in the ApiMutableModelControllerBase Class:
*
* `function getAction()`
*
* `function setAction()`
*
* @var string $internalModelClass
*/
protected static $internalModelClass = 'OPNsense\Sslh\Settings';
/**
* An API endpoint to call when no parameters are
* provided for the API. Can be used to test the API is working.
* API endpoint:
*
* `/api/sslh/settings`
*
* Usage:
*
* `/api/sslh/settings`
*
* Returns an array which gets converted to json in the POST response.
*
* @return array includes status, saying everything is A-OK
*/
public function indexAction()
{
return array('status' => 'ok');
}
}
@@ -0,0 +1,64 @@
<?php
/*
Copyright (C) 2022 agh1467@protonmail.com
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\Sslh;
/**
* An IndexController-based class that creates an endpoint to display the Settings
* page in the UI.
*
* @package OPNsense\Sslh
*/
class SettingsController extends \OPNsense\Base\IndexController
{
/**
* This function creates an endpoint in the UI for the Settings Controller.
*
* UI endpoint:
* `/ui/sslh/settings`
*
* This is the default action when no parameters are provided.
*/
public function indexAction()
{
// Set environment variables for within the Volt templates.
$this->view->setVars(
[
'plugin_name' => 'sslh',
'api_name' => 'sslh',
'this_form' => $this->getForm('settings'),
// controllers/OPNsense/Sslh/forms/settings.xml
]
);
// pick the template as the next view to render
$this->view->pick('OPNsense/Sslh/settings');
// views/OPNsense/Sslh/settings.volt
}
}
@@ -0,0 +1,101 @@
<form>
<field>
<id>settings.enabled</id>
<label>Enable SSLH</label>
<type>checkbox</type>
<help>This will enable the SSLH service.</help>
</field>
<field>
<id>settings.listen_addresses</id>
<label>Listen Addresses</label>
<help>Hostname (or IP address) and port combination on which to listen, e.g. localhost:443, 10.5.0.1:443 (typically these resolve to-, or specify, a WAN IP address). This can be defined multiple times to bind sslh to several addresses.</help>
<style>tokenize</style>
<allownew>true</allownew>
<type>select_multiple</type>
</field>
<field>
<id>settings.mode</id>
<label>Mode</label>
<help>Select the mode in which to run sslh: fork - stable but slow performance | select - new but high performance</help>
<type>dropdown</type>
<advanced>true</advanced>
</field>
<field>
<id>settings.timeout</id>
<label>Timeout</label>
<help>Timeout in seconds before forwarding the connection to the timeout protocol (which should usually be SSH). Default: 2</help>
<hint>2</hint>
<type>text</type>
<advanced>true</advanced>
</field>
<field>
<id>settings.on_timeout</id>
<label>On Timeout</label>
<help>Name of the protocol to connect to after the timeout period is over. Default is to forward to the first specified protocol. It usually makes sense to specify 'ssh' as the timeout protocol, as the SSH specification does not tell who is supposed to speak first and a large number of SSH clients wait for the server to send its banner. Default: ssh</help>
<type>dropdown</type>
<advanced>true</advanced>
</field>
<field>
<id>settings.ssh_target</id>
<label>SSH Target</label>
<help>Interface and port on which to forward SSH connections, typically localhost:22.</help>
<hint>localhost:22</hint>
<type>text</type>
</field>
<field>
<id>settings.tls_target</id>
<label>SSL/TLS Target</label>
<help>Interface and port on which to forward SSL connection, typically localhost:443. Note that you can set sslh to listen on ext_ip:443 and httpd to listen on localhost:443: this allows clients inside your network to just connect directly to httpd. Also, sslh probes for SSLv3 (or TLSv1) handshake and will reject connections from clients requesting SSLv2. This is compliant with RFC6176 which prohibits the usage of SSLv2. If you wish to accept SSLv2, use --anyprot instead.</help>
<hint>localhost:443</hint>
<type>text</type>
</field>
<field>
<id>settings.openvpn_target</id>
<label>OpenVPN Target</label>
<help>Interface and port on which to forward OpenVPN connections, typically localhost:1194.</help>
<hint>localhost:1194</hint>
<type>text</type>
</field>
<field>
<id>settings.http_target</id>
<label>HTTP Target</label>
<help>Interface and port on which to forward HTTP connections, typically localhost:80.</help>
<hint>localhost:80</hint>
<type>text</type>
</field>
<field>
<id>settings.xmpp_target</id>
<label>XMPP Target</label>
<help>Interface and port on which to forward XMPP connections, typically localhost:5222.</help>
<hint>localhost:5222</hint>
<type>text</type>
</field>
<field>
<id>settings.tinc_target</id>
<label>Tinc Target</label>
<help>Interface and port on which to forward Tinc connections, typically localhost:655.</help>
<hint>localhost:655</hint>
<type>text</type>
</field>
<field>
<id>settings.anyprot_target</id>
<label>Anyprot Target</label>
<hint></hint>
<help>Interface and port on which to forward if no other protocol has been found. Because sslh tries protocols in the order specified on the command line, this should be specified last. If no default is specified, sslh will forward unknown protocols to the first protocol specified.</help>
<type>text</type>
</field>
<field>
<id>settings.verbose</id>
<label>Verbose</label>
<help>Increase logging verboseness.</help>
<advanced>true</advanced>
<type>checkbox</type>
</field>
<field>
<id>settings.numeric</id>
<label>Numeric</label>
<help>Do not attempt to resolve hostnames: logs will contain IP addresses. This is mostly useful if the system's DNS is slow and running the sslh-select variant, as DNS requests will hang all connections. Default: false</help>
<type>checkbox</type>
<advanced>true</advanced>
</field>
</form>
@@ -0,0 +1,9 @@
<acl>
<page-services-sslh>
<name>Services: SSLH</name>
<patterns>
<pattern>ui/sslh/*</pattern>
<pattern>api/sslh/*</pattern>
</patterns>
</page-services-sslh>
</acl>
@@ -0,0 +1,6 @@
<menu>
<Services>
<Sslh cssClass="fa fa-lock fa-fw" url="/ui/sslh/settings">
</Sslh>
</Services>
</menu>
@@ -0,0 +1,43 @@
<?php
/*
Copyright (C) 2022 agh1467@protonmail.com
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\Sslh;
use OPNsense\Base\BaseModel;
/**
* Class Settings is a BaseModel class used when retriving model data
* via getModel().
*
* Functionality of this class is inherited entirely from BaseModel.
*
* @package OPNsense\Sslh
*/
class Settings extends BaseModel
{
}
@@ -0,0 +1,66 @@
<model>
<mount>//OPNsense/sslh</mount>
<version>0.0.1</version>
<items>
<enabled type="BooleanField">
<required>Y</required>
</enabled>
<listen_addresses type="CSVListField">
<required>Y</required>
<default>localhost:443</default>
<validationmessage>Please enter at least one hostname/IP:port combination.</validationmessage>
</listen_addresses>
<mode type="OptionField">
<required>Y</required>
<default>fork</default>
<Multiple>N</Multiple>
<OptionValues>
<option value="fork">fork</option>
<option value="select">select</option>
</OptionValues>
</mode>
<timeout type="IntegerField">
<required>N</required>
</timeout>
<tls_target type="TextField">
<required>N</required>
</tls_target>
<ssh_target type="TextField">
<required>N</required>
</ssh_target>
<openvpn_target type="TextField">
<required>N</required>
</openvpn_target>
<http_target type="TextField">
<required>N</required>
</http_target>
<xmpp_target type="TextField">
<required>N</required>
</xmpp_target>
<tinc_target type="TextField">
<required>N</required>
</tinc_target>
<anyprot_target type="TextField">
<required>N</required>
</anyprot_target>
<on_timeout type="OptionField">
<required>Y</required>
<default>ssh</default>
<Multiple>N</Multiple>
<OptionValues>
<option value="ssh">SSH</option>
<option value="openvpn">OpenVPN</option>
<option value="xmpp">XMPP</option>
<option value="http">HTTP</option>
<option value="tls">TLS</option>
<option value="anyprot">Anyprot</option>
</OptionValues>
</on_timeout>
<verbose type="BooleanField">
<required>N</required>
</verbose>
<numeric type="BooleanField">
<required>N</required>
</numeric>
</items>
</model>
@@ -0,0 +1,76 @@
{##
#
# OPNsense® is Copyright © 2014 2018 by Deciso B.V.
# This file is Copyright © 2022 agh1467@protonmail.com
# 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.
#}
{##
# This is the template for the settings page.
#
# This is the main page for this plugin.
#
# Variables sent in by the controller:
# plugin_name string name of this plugin, used for API calls
# this_form array the form XML in an array
#}
<div class="tab-content content-box tab-content">
<div id="settings" class="tab-pane fade in active">
<div class="content-box" style="padding-bottom: 1.5em;">
{{ partial("layout_partials/base_form",['fields':this_form,'id':'frm_settings'])}}
<div class="col-md-12">
<hr />
<button class="btn btn-primary" id="saveAct" type="button">
<b>{{ lang._('Save and Apply') }}</b>
<i id="saveAct_progress"></i>
</button>
</div>
</div>
</div>
</div>
<script>
$( document ).ready(function() {
var data_get_map = {'frm_settings':"/api/sslh/settings/get"};
mapDataToFormUI(data_get_map).done(function(data){
formatTokenizersUI();
$('.selectpicker').selectpicker('refresh');
});
$("#saveAct").click(function(){
saveFormToEndpoint(url="/api/{{ api_name }}/settings/set", formid='frm_settings',callback_ok=function(){
$("#saveAct_progress").addClass("fa fa-spinner fa-pulse");
ajaxCall(url="/api/{{ api_name }}/service/reconfigure", sendData={}, callback=function(data,status) {
updateServiceControlUI('{{ plugin_name }}');
$("#saveAct_progress").removeClass("fa fa-spinner fa-pulse");
});
});
});
updateServiceControlUI('{{ plugin_name }}');
});
</script>
@@ -0,0 +1,31 @@
################################################################################
# Service Actions #
################################################################################
[status]
command:/usr/local/etc/rc.d/sslh status || exit 0
parameters:
type:script_output
message: sslh: requesting status
description:
[start]
command:/usr/local/etc/rc.d/sslh start
parameters:
type:script
message: sslh: starting
description: sslh: Start service
[stop]
command:/usr/local/etc/rc.d/sslh stop
parameters:
type:script
message: sslh: stopping
description: sslh: Stop service
[restart]
command:/usr/local/etc/rc.d/sslh restart
parameters:
type:script
message: sslh: restarting
description: sslh: Restart service
@@ -0,0 +1,2 @@
sslh.jinja:/etc/rc.conf.d/sslh
sslh.conf.jinja:/usr/local/etc/sslh.conf
@@ -0,0 +1,121 @@
{##
#
# OPNsense® is Copyright © 2014 2018 by Deciso B.V.
# This file is Copyright © 2022 agh1467@protonmail.com
# 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.
#}
{#- /usr/local/etc/sslh.conf -#}
{% set plugin_name = 'sslh' %}
{# Prevent configd reload from erroring out if node doesn't exist #}
{% if OPNsense[plugin_name] is defined %}
{% set cfg = OPNsense[plugin_name] %}
################################################################################
# #
# sslh configuration file for v1.21c #
# #
################################################################################
{# ------------ Static options, should remain unchanged by the user. -------- #}
{# standard location for pid files #}
pidfile:"/var/run/sslh.pid";
{# Run as nobody for security reasons. #}
user: "nobody";
{# ------------ Dynamic options, user configurable. -------------------------- #}
{% if cfg['verbose'] is defined %}
verbose:{{ '1' if (cfg['verbose'] == '1') else '0' }};
{% endif -%}
{% if cfg['numeric'] is defined %}
numeric:{{ 'true' if (cfg['numeric'] == '1') else 'false' }};
{% endif -%}
{% if cfg['timeout'] is defined %}
timeout:{{ cfg['timeout'] }};
{% endif -%}
{% if cfg['on_timeout'] is defined %}
on_timeout:"{{ cfg['on_timeout'] }}";
{% endif -%}
{% if cfg['listen_addresses'] is defined %}
{% set listen_list = [] %}
{# # listen_addresses is comma delimited string, split() to iterate through. #}
{% for for_listen in cfg['listen_addresses'].split(',') %}
{# # Need to further split the listen address by hostname:port #}
{% set listen_hostname, separator, listen_port = for_listen.rpartition(':') %}
{% if listen_hostname != '' and listen_port != '' %}
{% do listen_list.append(' { host: "'~listen_hostname~'"; port: "'~listen_port~'" }') %}
{% endif %}
{% endfor %}
{% if listen_list != [] -%}{# Don't put this setting unless we have listen addreses to put. #}
listen:
(
{{ listen_list|join(",\n") }}
);
{% endif %}
{% endif %}
{# All of the protocols #}
protocols:
(
{% if cfg['ssh_target'] is defined %}
{% set hostname, separator, port = cfg['ssh_target'].rpartition(':') %}
{% if hostname != '' and port != '' %}
{ name: "ssh"; service: "ssh"; host: "{{ hostname }}"; port: "{{ port }}"; },
{% endif %}
{% endif %}
{% if cfg['openvpn_target'] is defined %}
{% set hostname, separator, port = cfg['openvpn_target'].rpartition(':') %}
{% if hostname != '' and port != '' %}
{ name: "openvpn"; host: "{{ hostname }}"; port: "{{ port }}"; },
{% endif %}
{% endif %}
{% if cfg['xmpp_target'] is defined %}
{% set hostname, separator, port = cfg['xmpp_target'].rpartition(':') %}
{% if hostname != '' and port != '' %}
{ name: "xmpp"; host: "{{ hostname }}"; port: "{{ port }}"; },
{% endif %}
{% endif %}
{% if cfg['http_target'] is defined %}
{% set hostname, separator, port = cfg['http_target'].rpartition(':') %}
{% if hostname != '' and port != '' %}
{ name: "http"; host: "{{ hostname }}"; port: "{{ port }}"; },
{% endif %}
{% endif %}
{% if cfg['tls_target'] is defined %}
{% set hostname, separator, port = cfg['tls_target'].rpartition(':') %}
{% if hostname != '' and port != '' %}
{ name: "tls"; host: "{{ hostname }}"; port: "{{ port }}"; },
{% endif %}
{% endif %}
{% if cfg['anyprot_target'] is defined %}
{% set hostname, separator, port = cfg['anyprot_target'].rpartition(':') %}
{% if hostname != '' and port != '' %}
{ name: "anyprot"; host: "{{ hostname }}"; port: "{{ port }}"; },
{% endif %}
{% endif %}
);
{% endif %}
@@ -0,0 +1,11 @@
{# /usr/local/etc/rc.conf.d/sslh #}
{% set plugin_name = 'sslh' %}
{% if OPNsense[plugin_name] is defined %}{# Prevent configd reload from erroring out if node doesn't exist #}
{% set cfg = OPNsense[plugin_name] %}
{% if cfg['enabled'] is defined %}
sslh_enable={{ '"YES"' if (cfg['enabled'] == '1') else '"NO"' }}
{% endif %}
{% if cfg['mode'] is defined %}
sslh_mode={{ '"fork"' if (cfg['mode'] == 'fork') else '"select"' }}
{% endif %}
{% endif %}