From 1652bc6935b2210c6283d475e21483a95e4e3577 Mon Sep 17 00:00:00 2001 From: Ad Schellevis Date: Tue, 10 Mar 2020 14:48:10 +0100 Subject: [PATCH] os-firewall: add support for "cancelation tokens" apply endpoint can request a rollback point, which will be reverted to if there's no call on cancelRollback with the same timestamp within 60 seconds. for https://github.com/opnsense/plugins/issues/1720 --- .../Firewall/Api/FilterController.php | 15 +++++- .../app/models/OPNsense/Firewall/Filter.php | 5 +- .../opnsense/scripts/pfplugin/rollback_cancel | 40 ++++++++++++++ .../opnsense/scripts/pfplugin/rollback_timer | 54 +++++++++++++++++++ .../conf/actions.d/actions_pfplugin.conf | 11 ++++ 5 files changed, 123 insertions(+), 2 deletions(-) create mode 100755 net/firewall/src/opnsense/scripts/pfplugin/rollback_cancel create mode 100755 net/firewall/src/opnsense/scripts/pfplugin/rollback_timer create mode 100644 net/firewall/src/opnsense/service/conf/actions.d/actions_pfplugin.conf diff --git a/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/FilterController.php b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/FilterController.php index 4c06d3e39..62116a88d 100644 --- a/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/FilterController.php +++ b/net/firewall/src/opnsense/mvc/app/controllers/OPNsense/Firewall/Api/FilterController.php @@ -66,15 +66,28 @@ class FilterController extends ApiMutableModelControllerBase return $this->toggleBase("rules.rule", $uuid, $enabled); } - public function applyAction() + public function applyAction($rollback_revision = null) { if ($this->request->isPost()) { + if ($rollback_revision != null) { + // background rollback timer + (new Backend())->configdpRun('pfplugin rollback_timer', [$rollback_revision], true); + } return array("status" => (new Backend())->configdRun('filter reload')); } else { return array("status" => "error"); } } + public function cancelRollbackAction($rollback_revision) + { + if ($this->request->isPost()) { + return array("status" => (new Backend())->configdpRun('pfplugin cancel_rollback', [$rollback_revision])); + } else { + return array("status" => "error"); + } + } + public function savepointAction() { if ($this->request->isPost()) { diff --git a/net/firewall/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.php b/net/firewall/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.php index 5294bb4d4..ccaa644ed 100644 --- a/net/firewall/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.php +++ b/net/firewall/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.php @@ -88,6 +88,7 @@ class Filter extends BaseModel * Rollback this model to a previous version. * Make sure to remove this object afterwards, since its contents won't be updated. * @param $revision float|string revision number + * @return bool action performed (backup revision existed) */ public function rollback($revision) { @@ -102,7 +103,9 @@ class Filter extends BaseModel $node = $targetdom->ownerDocument->importNode($sourcedom, TRUE); $targetdom->parentNode->replaceChild($node, $targetdom); Config::getInstance()->save(); + return true; } } + return false; } -} \ No newline at end of file +} diff --git a/net/firewall/src/opnsense/scripts/pfplugin/rollback_cancel b/net/firewall/src/opnsense/scripts/pfplugin/rollback_cancel new file mode 100755 index 000000000..c47f74b07 --- /dev/null +++ b/net/firewall/src/opnsense/scripts/pfplugin/rollback_cancel @@ -0,0 +1,40 @@ +#!/usr/local/bin/php += 2) { + $revision = preg_replace("/[^0-9.]/", "", $argv[1]); + if (!empty($revision)) { + $lckfile = "/tmp/pfplugin_{$revision}.lock"; + if (file_exists($lckfile)) { + unlink($lckfile); + exit(0); + } + } +} +exit(1); diff --git a/net/firewall/src/opnsense/scripts/pfplugin/rollback_timer b/net/firewall/src/opnsense/scripts/pfplugin/rollback_timer new file mode 100755 index 000000000..819a1f12c --- /dev/null +++ b/net/firewall/src/opnsense/scripts/pfplugin/rollback_timer @@ -0,0 +1,54 @@ +#!/usr/local/bin/php += 2) { + $revision = preg_replace("/[^0-9.]/", "", $argv[1]); + if (!empty($revision)) { + $lckfile = "/tmp/pfplugin_{$revision}.lock"; + file_put_contents($lckfile, ""); + // give the api 60 seconds to callback + for ($i=0; $i < 60 ; ++$i) { + if (!file_exists($lckfile)) { + // got feedback + exit(0); + } + sleep(1); + } + @unlink($lckfile); + // no feedback, revert + $mdlFilter = new OPNsense\Firewall\Filter(); + if ($mdlFilter->rollback($revision)) { + (new OPNsense\Core\Backend())->configdRun('filter reload'); + } else { + syslog(LOG_WARNING, "unable to revert to unexisting revision : {$revision}"); + } + } +} diff --git a/net/firewall/src/opnsense/service/conf/actions.d/actions_pfplugin.conf b/net/firewall/src/opnsense/service/conf/actions.d/actions_pfplugin.conf new file mode 100644 index 000000000..61a13f477 --- /dev/null +++ b/net/firewall/src/opnsense/service/conf/actions.d/actions_pfplugin.conf @@ -0,0 +1,11 @@ +[rollback_timer] +command:/usr/local/bin/flock -n -E 0 -o /tmp/pfplugin_rollback_timer.lock /usr/local/opnsense/scripts/pfplugin/rollback_timer +parameters: %s +type:script +message:wait for api feedback or revert to previous filter plugin config + +[cancel_rollback] +command: /usr/local/opnsense/scripts/pfplugin/rollback_cancel +parameters: %s +type:script_output +message:cancel pfplugin rollback