Preserve independent NordVPN enrollment selectors
This commit is contained in:
parent
aaf5d18f58
commit
2890153002
8 changed files with 182 additions and 90 deletions
|
|
@ -51,9 +51,16 @@ function nordvpn_firewall(Plugin $firewall)
|
|||
return;
|
||||
}
|
||||
|
||||
$interfaces = nordvpn_values((string)$settings->general->interfaces);
|
||||
$source = (string)$settings->general->source ?: 'any';
|
||||
$destination = (string)$settings->general->destination ?: 'any';
|
||||
$enrollments = [];
|
||||
$interfaces = [];
|
||||
foreach ($settings->enrollments->enrollment->iterateItems() as $enrollment) {
|
||||
if ((string)$enrollment->enabled !== '1') {
|
||||
continue;
|
||||
}
|
||||
$enrollments[] = $enrollment;
|
||||
$interfaces = array_merge($interfaces, nordvpn_values((string)$enrollment->interfaces));
|
||||
}
|
||||
$interfaces = array_values(array_unique($interfaces));
|
||||
$directGateway = (string)$settings->general->direct_gateway;
|
||||
$status = (new StatusStore())->read();
|
||||
$available = !empty($status['available']);
|
||||
|
|
@ -85,37 +92,40 @@ function nordvpn_firewall(Plugin $firewall)
|
|||
$firewall->registerFilterRule(190000, $rule);
|
||||
}
|
||||
|
||||
foreach ($interfaces as $interface) {
|
||||
$rule = nordvpn_filter_rule(
|
||||
$interface,
|
||||
$source,
|
||||
$destination,
|
||||
(string)$settings->general->source_not === '1',
|
||||
(string)$settings->general->destination_not === '1'
|
||||
);
|
||||
$rule['descr'] = 'NordVPN egress enrollment';
|
||||
if ($available) {
|
||||
$rule['gateway'] = ManagedConfiguration::GATEWAY;
|
||||
} elseif ((string)$settings->general->failure_policy === 'fail_open') {
|
||||
if ($directGateway !== '') {
|
||||
$rule['gateway'] = $directGateway;
|
||||
foreach ($enrollments as $enrollment) {
|
||||
$source = (string)$enrollment->source ?: 'any';
|
||||
$destination = (string)$enrollment->destination ?: 'any';
|
||||
foreach (nordvpn_values((string)$enrollment->interfaces) as $interface) {
|
||||
$rule = nordvpn_filter_rule(
|
||||
$interface,
|
||||
$source,
|
||||
$destination,
|
||||
(string)$enrollment->source_not === '1',
|
||||
(string)$enrollment->destination_not === '1'
|
||||
);
|
||||
$rule['descr'] = 'NordVPN egress enrollment';
|
||||
if ($available) {
|
||||
$rule['gateway'] = ManagedConfiguration::GATEWAY;
|
||||
} elseif ((string)$settings->general->failure_policy === 'fail_open') {
|
||||
if ($directGateway !== '') {
|
||||
$rule['gateway'] = $directGateway;
|
||||
}
|
||||
} else {
|
||||
$rule['type'] = 'block';
|
||||
unset($rule['statetype']);
|
||||
}
|
||||
} else {
|
||||
$rule['type'] = 'block';
|
||||
unset($rule['statetype']);
|
||||
$firewall->registerFilterRule(190100, $rule);
|
||||
}
|
||||
$firewall->registerFilterRule(190100, $rule);
|
||||
$firewall->registerSNatRule(40, [
|
||||
'interface' => ManagedConfiguration::INTERFACE,
|
||||
'ipprotocol' => 'inet',
|
||||
'from' => $source,
|
||||
'from_not' => (string)$enrollment->source_not === '1',
|
||||
'to' => $destination,
|
||||
'to_not' => (string)$enrollment->destination_not === '1',
|
||||
'descr' => 'NordVPN managed outbound NAT',
|
||||
]);
|
||||
}
|
||||
|
||||
$firewall->registerSNatRule(40, [
|
||||
'interface' => ManagedConfiguration::INTERFACE,
|
||||
'ipprotocol' => 'inet',
|
||||
'from' => $source,
|
||||
'from_not' => (string)$settings->general->source_not === '1',
|
||||
'to' => $destination,
|
||||
'to_not' => (string)$settings->general->destination_not === '1',
|
||||
'descr' => 'NordVPN managed outbound NAT',
|
||||
]);
|
||||
}
|
||||
|
||||
function nordvpn_filter_rule(string $interface, string $source, string $destination, bool $sourceNot, bool $destinationNot)
|
||||
|
|
|
|||
|
|
@ -68,10 +68,17 @@ class ServiceController extends ApiControllerBase
|
|||
public function statusAction(): array
|
||||
{
|
||||
$settings = new Settings();
|
||||
$warning = (new EnrollmentInspector())->includesPrivateDestinations(
|
||||
(string)$settings->general->destination,
|
||||
(string)$settings->general->destination_not === '1'
|
||||
);
|
||||
$inspector = new EnrollmentInspector();
|
||||
$warning = false;
|
||||
foreach ($settings->enrollments->enrollment->iterateItems() as $enrollment) {
|
||||
if ((string)$enrollment->enabled === '1' && $inspector->includesPrivateDestinations(
|
||||
(string)$enrollment->destination,
|
||||
(string)$enrollment->destination_not === '1'
|
||||
)) {
|
||||
$warning = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ['status' => 'ok', 'tunnel' => (new StatusStore())->read(), 'rfc1918_warning' => $warning];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,36 @@ class SettingsController extends ApiMutableModelControllerBase
|
|||
protected static $internalModelName = 'settings';
|
||||
protected static $internalModelClass = 'OPNsense\NordVPN\Settings';
|
||||
|
||||
public function searchEnrollmentAction()
|
||||
{
|
||||
return $this->searchBase('enrollments.enrollment');
|
||||
}
|
||||
|
||||
public function getEnrollmentAction($uuid = null)
|
||||
{
|
||||
return $this->getBase('enrollment', 'enrollments.enrollment', $uuid);
|
||||
}
|
||||
|
||||
public function addEnrollmentAction()
|
||||
{
|
||||
return $this->addBase('enrollment', 'enrollments.enrollment');
|
||||
}
|
||||
|
||||
public function setEnrollmentAction($uuid)
|
||||
{
|
||||
return $this->setBase('enrollment', 'enrollments.enrollment', $uuid);
|
||||
}
|
||||
|
||||
public function delEnrollmentAction($uuid)
|
||||
{
|
||||
return $this->delBase('enrollments.enrollment', $uuid);
|
||||
}
|
||||
|
||||
public function toggleEnrollmentAction($uuid, $enabled = null)
|
||||
{
|
||||
return $this->toggleBase('enrollments.enrollment', $uuid, $enabled);
|
||||
}
|
||||
|
||||
public function reconfigureAction()
|
||||
{
|
||||
if (!$this->request->isPost()) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ class IndexController extends \OPNsense\Base\IndexController
|
|||
public function indexAction()
|
||||
{
|
||||
$this->view->settingsForm = $this->getForm('settings');
|
||||
$this->view->enrollmentForm = $this->getForm('dialogEditEnrollment');
|
||||
$this->view->enrollmentGrid = $this->getFormGrid('dialogEditEnrollment');
|
||||
$this->view->pick('OPNsense/NordVPN/index');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
<form>
|
||||
<field>
|
||||
<id>enrollment.enabled</id>
|
||||
<label>Enabled</label>
|
||||
<type>checkbox</type>
|
||||
<grid_view>
|
||||
<width>100</width>
|
||||
<type>boolean</type>
|
||||
<formatter>rowtoggle</formatter>
|
||||
</grid_view>
|
||||
</field>
|
||||
<field>
|
||||
<id>enrollment.interfaces</id>
|
||||
<label>Interfaces</label>
|
||||
<type>select_multiple</type>
|
||||
<grid_view>
|
||||
<width>200</width>
|
||||
</grid_view>
|
||||
</field>
|
||||
<field>
|
||||
<id>enrollment.source</id>
|
||||
<label>Source</label>
|
||||
<type>select_multiple</type>
|
||||
<style>tokenize</style>
|
||||
<grid_view>
|
||||
<width>200</width>
|
||||
</grid_view>
|
||||
</field>
|
||||
<field>
|
||||
<id>enrollment.source_not</id>
|
||||
<label>Invert source</label>
|
||||
<type>checkbox</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>enrollment.destination</id>
|
||||
<label>Destination</label>
|
||||
<type>select_multiple</type>
|
||||
<style>tokenize</style>
|
||||
<grid_view>
|
||||
<width>200</width>
|
||||
</grid_view>
|
||||
</field>
|
||||
<field>
|
||||
<id>enrollment.destination_not</id>
|
||||
<label>Invert destination</label>
|
||||
<type>checkbox</type>
|
||||
</field>
|
||||
</form>
|
||||
|
|
@ -10,37 +10,6 @@
|
|||
<style>tokenize</style>
|
||||
<help>Leave empty to permit every country returned by NordVPN recommendations.</help>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>NordVPN egress enrollment</label>
|
||||
</field>
|
||||
<field>
|
||||
<id>settings.general.interfaces</id>
|
||||
<label>Interfaces</label>
|
||||
<type>select_multiple</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>settings.general.source</id>
|
||||
<label>Source</label>
|
||||
<type>select_multiple</type>
|
||||
<style>tokenize</style>
|
||||
</field>
|
||||
<field>
|
||||
<id>settings.general.source_not</id>
|
||||
<label>Invert source</label>
|
||||
<type>checkbox</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>settings.general.destination</id>
|
||||
<label>Destination</label>
|
||||
<type>select_multiple</type>
|
||||
<style>tokenize</style>
|
||||
</field>
|
||||
<field>
|
||||
<id>settings.general.destination_not</id>
|
||||
<label>Invert destination</label>
|
||||
<type>checkbox</type>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>Failure and direct egress</label>
|
||||
|
|
|
|||
|
|
@ -5,30 +5,6 @@
|
|||
<items>
|
||||
<general>
|
||||
<countries type="CSVListField"/>
|
||||
<interfaces type="InterfaceField">
|
||||
<Multiple>Y</Multiple>
|
||||
<Filters>
|
||||
<if>/^(?!lo0$).*/</if>
|
||||
</Filters>
|
||||
</interfaces>
|
||||
<source type="NetworkAliasField">
|
||||
<Default>any</Default>
|
||||
<Required>Y</Required>
|
||||
<Multiple>Y</Multiple>
|
||||
</source>
|
||||
<source_not type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</source_not>
|
||||
<destination type="NetworkAliasField">
|
||||
<Default>any</Default>
|
||||
<Required>Y</Required>
|
||||
<Multiple>Y</Multiple>
|
||||
</destination>
|
||||
<destination_not type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</destination_not>
|
||||
<direct_gateway type="JsonKeyValueStoreField">
|
||||
<ConfigdPopulateAct>interface gateways list -g</ConfigdPopulateAct>
|
||||
<ValidationMessage>Specify a valid IPv4 gateway.</ValidationMessage>
|
||||
|
|
@ -46,6 +22,39 @@
|
|||
<Required>Y</Required>
|
||||
</exempt_wireguard>
|
||||
</general>
|
||||
<enrollments>
|
||||
<enrollment type="ArrayField">
|
||||
<enabled type="BooleanField">
|
||||
<Default>1</Default>
|
||||
<Required>Y</Required>
|
||||
</enabled>
|
||||
<interfaces type="InterfaceField">
|
||||
<Required>Y</Required>
|
||||
<Multiple>Y</Multiple>
|
||||
<Filters>
|
||||
<if>/^(?!lo0$).*/</if>
|
||||
</Filters>
|
||||
</interfaces>
|
||||
<source type="NetworkAliasField">
|
||||
<Default>any</Default>
|
||||
<Required>Y</Required>
|
||||
<Multiple>Y</Multiple>
|
||||
</source>
|
||||
<source_not type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</source_not>
|
||||
<destination type="NetworkAliasField">
|
||||
<Default>any</Default>
|
||||
<Required>Y</Required>
|
||||
<Multiple>Y</Multiple>
|
||||
</destination>
|
||||
<destination_not type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</destination_not>
|
||||
</enrollment>
|
||||
</enrollments>
|
||||
<managed>
|
||||
<instance_uuid type="TextField"/>
|
||||
<peer_uuid type="TextField"/>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,15 @@
|
|||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#{{ enrollmentGrid['table_id'] }}').UIBootgrid({
|
||||
search: '/api/nordvpn/settings/searchEnrollment/',
|
||||
get: '/api/nordvpn/settings/getEnrollment/',
|
||||
set: '/api/nordvpn/settings/setEnrollment/',
|
||||
add: '/api/nordvpn/settings/addEnrollment/',
|
||||
del: '/api/nordvpn/settings/delEnrollment/',
|
||||
toggle: '/api/nordvpn/settings/toggleEnrollment/',
|
||||
options: {selection: false, multiSelect: false}
|
||||
});
|
||||
|
||||
function loadSettings() {
|
||||
ajaxCall('/api/nordvpn/service/countries', {}, function(data) {
|
||||
if (data.status === 'ok') {
|
||||
|
|
@ -76,6 +86,11 @@
|
|||
{{ lang._('NordVPN egress enrollment can include an RFC 1918 destination address.') }}
|
||||
</div>
|
||||
|
||||
<div class="content-box">
|
||||
<h2>{{ lang._('NordVPN egress enrollment') }}</h2>
|
||||
{{ partial('layout_partials/base_bootgrid_table', enrollmentGrid) }}
|
||||
</div>
|
||||
|
||||
<div class="content-box">
|
||||
{{ partial('layout_partials/base_form', ['fields': settingsForm, 'id': 'frm_NordVPNSettings']) }}
|
||||
<hr/>
|
||||
|
|
@ -84,6 +99,8 @@
|
|||
</button>
|
||||
</div>
|
||||
|
||||
{{ partial('layout_partials/base_dialog', ['fields': enrollmentForm, 'id': enrollmentGrid['edit_dialog_id'], 'label': lang._('Edit enrollment')]) }}
|
||||
|
||||
<div class="content-box">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
|
|
|
|||
Loading…
Reference in a new issue