Skip to content

Commit 07d5751

Browse files
Merge pull request #765 from jaredhendrickson13/next_patch
v2.6.3 Fixes
2 parents e46a9c3 + 61b5ba3 commit 07d5751

7 files changed

Lines changed: 145 additions & 5 deletions

File tree

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Fields/InterfaceField.inc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,8 @@ class InterfaceField extends StringField {
219219
foreach ($virtual_ips as $virtual_ip) {
220220
# Only include CARP virtual iPs with unique IDs
221221
if ($virtual_ip['mode'] === 'carp' and $virtual_ip['uniqid']) {
222-
$choices[$virtual_ip['uniqid']] = $virtual_ip['uniqid'];
222+
$uniqid = '_vip' . $virtual_ip['uniqid'];
223+
$choices[$uniqid] = $uniqid;
223224
}
224225
}
225226
}

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Models/IPsecPhase1.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class IPsecPhase1 extends Model {
9494
);
9595
$this->interface = new InterfaceField(
9696
required: true,
97+
allow_carp_interface: true,
9798
help_text: 'The interface for the local endpoint of this phase 1 entry. This should be an interface ' .
9899
'that is reachable by the remote peer.',
99100
);

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Models/OpenVPNClientExport.inc

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ class OpenVPNClientExport extends Model {
257257
return openvpn_client_export_config(
258258
srvid: $this->server->value,
259259
usrid: $this->username->value ? $this->username->get_related_model()->id : null,
260-
crtid: $this->certref->value ? $this->certref->get_related_model()->id : null,
260+
crtid: $this->locate_crtid(),
261261
useaddr: $this->useaddr_hostname->value ?? $this->useaddr->value,
262262
verifyservercn: $this->verifyservercn->value,
263263
blockoutsidedns: $this->blockoutsidedns->value,
@@ -291,7 +291,7 @@ class OpenVPNClientExport extends Model {
291291
return openvpn_client_export_installer(
292292
srvid: $this->server->value,
293293
usrid: $this->username->value ? $this->username->get_related_model()->id : null,
294-
crtid: $this->certref->value ? $this->certref->get_related_model()->id : null,
294+
crtid: $this->locate_crtid(),
295295
useaddr: $this->useaddr_hostnam->value ?? $this->useaddr->value,
296296
verifyservercn: $this->verifyservercn->value,
297297
blockoutsidedns: $this->blockoutsidedns->value,
@@ -319,7 +319,7 @@ class OpenVPNClientExport extends Model {
319319
return viscosity_openvpn_client_config_exporter(
320320
srvid: $this->server->value,
321321
usrid: $this->username->value ? $this->username->get_related_model()->id : null,
322-
crtid: $this->certref->value ? $this->certref->get_related_model()->id : null,
322+
crtid: $this->locate_crtid(),
323323
useaddr: $this->useaddr_hostname->value ?? $this->useaddr->value,
324324
verifyservercn: $this->verifyservercn->value,
325325
blockoutsidedns: $this->blockoutsidedns->value,
@@ -336,6 +336,42 @@ class OpenVPNClientExport extends Model {
336336
);
337337
}
338338

339+
/**
340+
* Obtains the crtid value pfSense expects for a given certref given the current OpenVPN server and username.
341+
* This is necessary because pfSense's openvpn_client_export_validate_config function sometimes expects
342+
* crtid to be the index of the 'cert' config path, and sometimes expects it to be the index of the
343+
* 'system/user/{$usrid}/cert' config path.
344+
* @returns int|null The crtid value pfSense expects for a given certref, or null if no certref is set.
345+
*/
346+
public function locate_crtid(): int|null {
347+
# If no certref is set, return null
348+
if (!$this->certref->value) {
349+
return null;
350+
}
351+
352+
# Load related models
353+
$ovpnsrv = $this->server->get_related_model();
354+
$user = $this->username->get_related_model();
355+
$cert = $this->certref->get_related_model();
356+
357+
# If a username was provided, the server is using server_tls_user and the local database, pfSense expects
358+
# the crtid to be the index of the 'system/user/{$usrid}/cert' config path not the 'cert' config path.
359+
if (
360+
$user and
361+
$ovpnsrv->mode->value === 'server_tls_user' and
362+
$ovpnsrv->authmode->value === ['Local Database']
363+
) {
364+
foreach ($user->cert->value as $idx => $user_cert) {
365+
if ($user_cert === $cert->refid->value) {
366+
return $idx;
367+
}
368+
}
369+
}
370+
371+
# Otherwise, just return the index of the certificate directly
372+
return $cert->id;
373+
}
374+
339375
/**
340376
* Obtains the proxy configuration array expected by pfSense functions.
341377
* @return array the proxy configuration array expected by pfSense functions

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Models/RESTAPISettings.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ class RESTAPISettings extends Model {
235235

236236
# Format choices so they include the class's verbose name as the choice's verbose name
237237
foreach (get_classes_from_namespace(namespace: '\\RESTAPI\\Models\\') as $model_class) {
238-
$model = new $model_class();
238+
$model = new $model_class(skip_init: true);
239239
foreach ($model->get_fields() as $field) {
240240
if ($model->$field->sensitive) {
241241
$choices[$model->get_class_shortname() . ':' . $field] =

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIFieldsInterfaceFieldTestCase.inc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace RESTAPI\Tests;
55
use RESTAPI\Core\TestCase;
66
use RESTAPI\Fields\InterfaceField;
77
use RESTAPI\Models\RESTAPISettings;
8+
use RESTAPI\Models\VirtualIP;
89

910
class APIFieldsInterfaceFieldTestCase extends TestCase {
1011
# TODO: Needs Tests to ensure CARP interfaces and interface groups become available choices when the
@@ -78,6 +79,30 @@ class APIFieldsInterfaceFieldTestCase extends TestCase {
7879
$this->assert_is_true(in_array('openvpn', $test_field->choices));
7980
}
8081

82+
/**
83+
* Checks that the InterfaceField's `allow_carp_interface` parameter allows CARP interfaces to become choices.
84+
*/
85+
public function test_get_interface_choices_with_allow_carp_interface(): void {
86+
# First, create a carp virtual IP to test with
87+
$vip = new VirtualIP(
88+
interface: 'lan',
89+
mode: 'carp',
90+
subnet: '127.0.0.99',
91+
subnet_bits: 32,
92+
vhid: 99,
93+
password: 'test',
94+
);
95+
$vip->create();
96+
97+
# Ensure the virtual IP is now a valid choice when `allow_carp_interface` is enabled
98+
$test_field = new InterfaceField(allow_carp_interface: true);
99+
$test_field->set_choices_from_callable();
100+
$this->assert_is_true(in_array("_vip{$vip->uniqid->value}", $test_field->choices));
101+
102+
# Cleanup
103+
$vip->delete();
104+
}
105+
81106
/**
82107
* Checks that the InterfaceField's to_internal() method correctly translates representation values to their
83108
* internal values.

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsOpenVPNClientExportTestCase.inc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,4 +441,38 @@ class APIModelsOpenVPNClientExportTestCase extends TestCase {
441441
$this->assert_is_true(!ctype_print($export->binary_data->value));
442442
$this->assert_is_false(file_exists("/tmp/{$export->filename->value}"));
443443
}
444+
445+
/**
446+
* Ensures we can exporta config for a server using the 'server_tls_user' mode, a user cert, and local database
447+
* auth. This is a regression test for issue #756. This is a necessary test distinction as pfSense will change the
448+
* meaning of the crtid passed into certain pfSense functions based on the server mode and authmode.
449+
*/
450+
public function test_create_with_server_tls_user_and_local_database(): void {
451+
# First, update the OpenVPNServer to use the 'server_tls_user' mode and local database auth
452+
$this->ovpns->mode->value = 'server_tls_user';
453+
$this->ovpns->authmode->value = ['Local Database'];
454+
$this->ovpns->update();
455+
456+
# Setup the export
457+
$export = new OpenVPNClientExport(
458+
id: $this->ovpnce->id,
459+
type: 'confinline',
460+
username: $this->user->name->value,
461+
certref: $this->user_cert->refid->value,
462+
);
463+
464+
# Ensure the certref's object ID does NOT match the determined crtid. These should not match because
465+
# pfSense now refers to the crtid as the index of the cert in the system/user config, NOT the cert config
466+
# like usual.
467+
$this->assert_not_equals($this->user_cert->refid->get_related_model()->id, $export->locate_crtid());
468+
469+
# Ensure we can complete the export as intended and that the embedded cert is correct
470+
$export->create();
471+
$this->assert_is_not_empty($export->binary_data->value);
472+
$this->assert_str_contains($export->binary_data->value, $this->user_cert->crt->value);
473+
474+
# Reset the server mode and authmode for other tests
475+
$this->ovpns->mode->value = 'server_tls';
476+
$this->ovpns->update();
477+
}
444478
}

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsVirtualIPTestCase.inc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,4 +283,47 @@ class APIModelsVirtualIPTestCase extends TestCase {
283283
# Clean up the VIP we created
284284
$vip->delete();
285285
}
286+
287+
/**
288+
* Ensures we can create an IP alias virtual IP nested under a parent CARP virtual IP.
289+
*/
290+
public function test_nested_vip_under_carp_vip(): void {
291+
# Create a CARP virtual IP to test with
292+
$carp_vip = new VirtualIP(
293+
interface: 'lan',
294+
mode: 'carp',
295+
subnet: '127.0.0.105',
296+
subnet_bits: 32,
297+
vhid: 105,
298+
password: 'test',
299+
async: false,
300+
);
301+
$carp_vip->create();
302+
303+
# Create a new IP alias virtual IP that uses the CARP virtual IP as its interface
304+
$child_vip = new VirtualIP(
305+
interface: "_vip{$carp_vip->uniqid->value}",
306+
mode: 'ipalias',
307+
subnet: '127.0.0.106',
308+
subnet_bits: 32,
309+
async: false,
310+
);
311+
$child_vip->create(apply: true);
312+
313+
# Ensure the parent interface shows both virtual IPs sharing the same vhid in ifconfig
314+
$iface = $carp_vip->interface->get_related_model()->if->value;
315+
$ifconfig = new Command("/sbin/ifconfig $iface");
316+
$this->assert_str_contains(
317+
$ifconfig->output,
318+
'inet 127.0.0.105 netmask 0xffffffff broadcast 127.0.0.105 vhid 105',
319+
);
320+
$this->assert_str_contains(
321+
$ifconfig->output,
322+
'inet 127.0.0.106 netmask 0xffffffff broadcast 127.0.0.106 vhid 105',
323+
);
324+
325+
# Clean up the VIPs we created
326+
$child_vip->delete();
327+
$carp_vip->delete(apply: true);
328+
}
286329
}

0 commit comments

Comments
 (0)