summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md7
-rw-r--r--data/config-mode-dependencies.json12
-rw-r--r--interface-definitions/include/bgp/afi-rd.xml.i2
-rw-r--r--interface-definitions/include/listen-address-single.xml.i1
-rw-r--r--op-mode-definitions/generate-ipsec-debug-archive.xml.in2
-rw-r--r--python/vyos/configdep.py48
-rw-r--r--python/vyos/frr.py2
-rwxr-xr-xscripts/check-pr-title-and-commit-messages.py2
-rwxr-xr-xsmoketest/scripts/cli/test_dependency_graph.py54
-rwxr-xr-xsmoketest/scripts/cli/test_pki.py22
-rwxr-xr-xsrc/conf_mode/firewall.py9
-rwxr-xr-xsrc/conf_mode/http-api.py8
-rwxr-xr-xsrc/conf_mode/interfaces-ethernet.py2
-rwxr-xr-xsrc/conf_mode/interfaces-virtual-ethernet.py13
-rwxr-xr-xsrc/conf_mode/pki.py71
-rwxr-xr-xsrc/conf_mode/protocols_mpls.py5
-rwxr-xr-xsrc/conf_mode/service_pppoe-server.py13
-rwxr-xr-xsrc/op_mode/generate_ipsec_debug_archive.py89
-rwxr-xr-xsrc/op_mode/generate_ipsec_debug_archive.sh36
-rwxr-xr-xsrc/op_mode/webproxy_update_blacklist.sh2
20 files changed, 299 insertions, 101 deletions
diff --git a/README.md b/README.md
index fcaad5c89..0e7daa491 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# vyos-1x: VyOS 1.2.0+ Configuration Scripts and Data
+# vyos-1x: VyOS command definitions, configuration scripts, and data
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=vyos%3Avyos-1x&metric=coverage)](https://sonarcloud.io/component_measures?id=vyos%3Avyos-1x&metric=coverage)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fvyos%2Fvyos-1x.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fvyos%2Fvyos-1x?ref=badge_shield)
@@ -36,7 +36,7 @@ src
## Interface/command definitions
Raw `node.def` files for the old backend are no longer written by hand or
-generated by custom sciprts. They are all now produced from a unified XML format
+generated by custom scripts. They are all now produced from a unified XML format
that supports a strict subset of the old backend features. In particular, it
intentionally does not support embedded shell scripts, default values, and value
"types", instead delegating those tasks to external scripts.
@@ -54,8 +54,7 @@ The guidelines in a nutshell:
generating taret config, see our
[documentation](https://docs.vyos.io/en/latest/contributing/development.html#python)
for the common structure
-* Use the `get_config_dict()` API as much as possible when retrieving values from
- the CLI
+* Use the `get_config_dict()` API as much as possible when retrieving values from the CLI
* Use a template processor when the format is more complex than just one line
(Jinja2 and pystache are acceptable options)
diff --git a/data/config-mode-dependencies.json b/data/config-mode-dependencies.json
new file mode 100644
index 000000000..9e943ba2c
--- /dev/null
+++ b/data/config-mode-dependencies.json
@@ -0,0 +1,12 @@
+{
+ "firewall": {"group_resync": ["nat", "policy-route"]},
+ "http_api": {"https": ["https"]},
+ "pki": {
+ "ethernet": ["interfaces-ethernet"],
+ "openvpn": ["interfaces-openvpn"],
+ "https": ["https"],
+ "ipsec": ["vpn_ipsec"],
+ "openconnect": ["vpn_openconnect"],
+ "sstp": ["vpn_sstp"]
+ }
+}
diff --git a/interface-definitions/include/bgp/afi-rd.xml.i b/interface-definitions/include/bgp/afi-rd.xml.i
index 767502094..beb1447df 100644
--- a/interface-definitions/include/bgp/afi-rd.xml.i
+++ b/interface-definitions/include/bgp/afi-rd.xml.i
@@ -17,7 +17,7 @@
<description>Route Distinguisher, (x.x.x.x:yyy|xxxx:yyyy)</description>
</valueHelp>
<constraint>
- <regex>((25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)(\.(25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)){3}|[0-9]{1,10}):[0-9]{1,5}</regex>
+ <validator name="bgp-rd-rt" argument="--route-distinguisher"/>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/include/listen-address-single.xml.i b/interface-definitions/include/listen-address-single.xml.i
index b5841cabb..30293b338 100644
--- a/interface-definitions/include/listen-address-single.xml.i
+++ b/interface-definitions/include/listen-address-single.xml.i
@@ -1,3 +1,4 @@
+<!-- include start from listen-address-single.xml.i -->
<leafNode name="listen-address">
<properties>
<help>Local IP addresses to listen on</help>
diff --git a/op-mode-definitions/generate-ipsec-debug-archive.xml.in b/op-mode-definitions/generate-ipsec-debug-archive.xml.in
index f268d5ae5..a9ce113d1 100644
--- a/op-mode-definitions/generate-ipsec-debug-archive.xml.in
+++ b/op-mode-definitions/generate-ipsec-debug-archive.xml.in
@@ -8,7 +8,7 @@
<properties>
<help>Generate IPSec debug-archive</help>
</properties>
- <command>${vyos_op_scripts_dir}/generate_ipsec_debug_archive.sh</command>
+ <command>sudo ${vyos_op_scripts_dir}/generate_ipsec_debug_archive.py</command>
</node>
</children>
</node>
diff --git a/python/vyos/configdep.py b/python/vyos/configdep.py
index e6b82ca93..d4b2cc78f 100644
--- a/python/vyos/configdep.py
+++ b/python/vyos/configdep.py
@@ -14,11 +14,21 @@
# along with this library. If not, see <http://www.gnu.org/licenses/>.
import os
+import json
+import typing
from inspect import stack
from vyos.util import load_as_module
+from vyos.defaults import directories
+from vyos.configsource import VyOSError
+from vyos import ConfigError
-dependents = {}
+# https://peps.python.org/pep-0484/#forward-references
+# for type 'Config'
+if typing.TYPE_CHECKING:
+ from vyos.config import Config
+
+dependent_func: dict[str, list[typing.Callable]] = {}
def canon_name(name: str) -> str:
return os.path.splitext(name)[0].replace('-', '_')
@@ -30,9 +40,22 @@ def canon_name_of_path(path: str) -> str:
def caller_name() -> str:
return stack()[-1].filename
-def run_config_mode_script(script: str, config):
- from vyos.defaults import directories
+def read_dependency_dict() -> dict:
+ path = os.path.join(directories['data'],
+ 'config-mode-dependencies.json')
+ with open(path) as f:
+ d = json.load(f)
+ return d
+
+def get_dependency_dict(config: 'Config') -> dict:
+ if hasattr(config, 'cached_dependency_dict'):
+ d = getattr(config, 'cached_dependency_dict')
+ else:
+ d = read_dependency_dict()
+ setattr(config, 'cached_dependency_dict', d)
+ return d
+def run_config_mode_script(script: str, config: 'Config'):
path = os.path.join(directories['conf_mode'], script)
name = canon_name(script)
mod = load_as_module(name, path)
@@ -46,20 +69,27 @@ def run_config_mode_script(script: str, config):
except (VyOSError, ConfigError) as e:
raise ConfigError(repr(e))
-def def_closure(script: str, config):
+def def_closure(target: str, config: 'Config',
+ tagnode: typing.Optional[str] = None) -> typing.Callable:
+ script = target + '.py'
def func_impl():
+ if tagnode:
+ os.environ['VYOS_TAGNODE_VALUE'] = tagnode
run_config_mode_script(script, config)
return func_impl
-def set_dependent(target: str, config):
+def set_dependents(case: str, config: 'Config',
+ tagnode: typing.Optional[str] = None):
+ d = get_dependency_dict(config)
k = canon_name_of_path(caller_name())
- l = dependents.setdefault(k, [])
- func = def_closure(target, config)
- l.append(func)
+ l = dependent_func.setdefault(k, [])
+ for target in d[k][case]:
+ func = def_closure(target, config, tagnode)
+ l.append(func)
def call_dependents():
k = canon_name_of_path(caller_name())
- l = dependents.get(k, [])
+ l = dependent_func.get(k, [])
while l:
f = l.pop(0)
f()
diff --git a/python/vyos/frr.py b/python/vyos/frr.py
index 0ffd5cba9..ccb132dd5 100644
--- a/python/vyos/frr.py
+++ b/python/vyos/frr.py
@@ -477,7 +477,7 @@ class FRRConfig:
# for the listed FRR issues above
pass
if count >= count_max:
- raise ConfigurationNotValid(f'Config commit retry counter ({count_max}) exceeded')
+ raise ConfigurationNotValid(f'Config commit retry counter ({count_max}) exceeded for {daemon} dameon!')
# Save configuration to /run/frr/config/frr.conf
save_configuration()
diff --git a/scripts/check-pr-title-and-commit-messages.py b/scripts/check-pr-title-and-commit-messages.py
index 3317745d6..9801b7456 100755
--- a/scripts/check-pr-title-and-commit-messages.py
+++ b/scripts/check-pr-title-and-commit-messages.py
@@ -7,7 +7,7 @@ import requests
from pprint import pprint
# Use the same regex for PR title and commit messages for now
-title_regex = r'^(([a-zA-Z.]+:\s)?)T\d+:\s+[^\s]+.*'
+title_regex = r'^(([a-zA-Z\-_.]+:\s)?)T\d+:\s+[^\s]+.*'
commit_regex = title_regex
def check_pr_title(title):
diff --git a/smoketest/scripts/cli/test_dependency_graph.py b/smoketest/scripts/cli/test_dependency_graph.py
new file mode 100755
index 000000000..45a40acc4
--- /dev/null
+++ b/smoketest/scripts/cli/test_dependency_graph.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import json
+import unittest
+from graphlib import TopologicalSorter, CycleError
+
+DEP_FILE = '/usr/share/vyos/config-mode-dependencies.json'
+
+def graph_from_dict(d):
+ g = {}
+ for k in list(d):
+ g[k] = set()
+ # add the dependencies for every sub-case; should there be cases
+ # that are mutally exclusive in the future, the graphs will be
+ # distinguished
+ for el in list(d[k]):
+ g[k] |= set(d[k][el])
+ return g
+
+class TestDependencyGraph(unittest.TestCase):
+ def setUp(self):
+ with open(DEP_FILE) as f:
+ dd = json.load(f)
+ self.dependency_graph = graph_from_dict(dd)
+
+ def test_cycles(self):
+ ts = TopologicalSorter(self.dependency_graph)
+ out = None
+ try:
+ # get node iterator
+ order = ts.static_order()
+ # try iteration
+ _ = [*order]
+ except CycleError as e:
+ out = e.args
+
+ self.assertIsNone(out)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_pki.py b/smoketest/scripts/cli/test_pki.py
index cba5ffdde..b18b0b039 100755
--- a/smoketest/scripts/cli/test_pki.py
+++ b/smoketest/scripts/cli/test_pki.py
@@ -246,5 +246,27 @@ class TestPKI(VyOSUnitTestSHIM.TestCase):
self.cli_delete(['service', 'https', 'certificates', 'certificate'])
+ def test_certificate_eapol_update(self):
+ self.cli_set(base_path + ['certificate', 'smoketest', 'certificate', valid_ca_cert.replace('\n','')])
+ self.cli_set(base_path + ['certificate', 'smoketest', 'private', 'key', valid_ca_private_key.replace('\n','')])
+ self.cli_commit()
+
+ self.cli_set(['interfaces', 'ethernet', 'eth1', 'eapol', 'certificate', 'smoketest'])
+ self.cli_commit()
+
+ cert_data = None
+
+ with open('/run/wpa_supplicant/eth1_cert.pem') as f:
+ cert_data = f.read()
+
+ self.cli_set(base_path + ['certificate', 'smoketest', 'certificate', valid_update_cert.replace('\n','')])
+ self.cli_set(base_path + ['certificate', 'smoketest', 'private', 'key', valid_update_private_key.replace('\n','')])
+ self.cli_commit()
+
+ with open('/run/wpa_supplicant/eth1_cert.pem') as f:
+ self.assertNotEqual(cert_data, f.read())
+
+ self.cli_delete(['interfaces', 'ethernet', 'eth1', 'eapol'])
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py
index ae3dd6414..f68acfe02 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/firewall.py
@@ -26,7 +26,7 @@ from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configdict import node_changed
from vyos.configdiff import get_config_diff, Diff
-from vyos.configdep import set_dependent, call_dependents
+from vyos.configdep import set_dependents, call_dependents
# from vyos.configverify import verify_interface_exists
from vyos.firewall import fqdn_config_parse
from vyos.firewall import geoip_update
@@ -162,11 +162,8 @@ def get_config(config=None):
firewall['group_resync'] = bool('group' in firewall or node_changed(conf, base + ['group']))
if firewall['group_resync']:
- # Update nat as firewall groups were updated
- set_dependent(nat_conf_script, conf)
- # Update policy route as firewall groups were updated
- set_dependent(policy_route_conf_script, conf)
-
+ # Update nat and policy-route as firewall groups were updated
+ set_dependents('group_resync', conf)
if 'config_trap' in firewall and firewall['config_trap'] == 'enable':
diff = get_config_diff(conf)
diff --git a/src/conf_mode/http-api.py b/src/conf_mode/http-api.py
index be80613c6..6328294c1 100755
--- a/src/conf_mode/http-api.py
+++ b/src/conf_mode/http-api.py
@@ -25,6 +25,7 @@ import vyos.defaults
from vyos.config import Config
from vyos.configdict import dict_merge
+from vyos.configdep import set_dependents, call_dependents
from vyos.template import render
from vyos.util import cmd
from vyos.util import call
@@ -61,6 +62,11 @@ def get_config(config=None):
else:
conf = Config()
+ # reset on creation/deletion of 'api' node
+ https_base = ['service', 'https']
+ if conf.exists(https_base):
+ set_dependents("https", conf)
+
base = ['service', 'https', 'api']
if not conf.exists(base):
return None
@@ -132,7 +138,7 @@ def apply(http_api):
# Let uvicorn settle before restarting Nginx
sleep(1)
- cmd(f'{vyos_conf_scripts_dir}/https.py', raising=ConfigError)
+ call_dependents()
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py
index e02841831..b49c945cd 100755
--- a/src/conf_mode/interfaces-ethernet.py
+++ b/src/conf_mode/interfaces-ethernet.py
@@ -175,7 +175,7 @@ def generate(ethernet):
loaded_pki_cert = load_certificate(pki_cert['certificate'])
loaded_ca_certs = {load_certificate(c['certificate'])
- for c in ethernet['pki']['ca'].values()}
+ for c in ethernet['pki']['ca'].values()} if 'ca' in ethernet['pki'] else {}
cert_full_chain = find_chain(loaded_pki_cert, loaded_ca_certs)
diff --git a/src/conf_mode/interfaces-virtual-ethernet.py b/src/conf_mode/interfaces-virtual-ethernet.py
index 53422ad2d..8efe89c41 100755
--- a/src/conf_mode/interfaces-virtual-ethernet.py
+++ b/src/conf_mode/interfaces-virtual-ethernet.py
@@ -68,12 +68,21 @@ def verify(veth):
if 'peer_name' not in veth:
raise ConfigError(f'Remote peer name must be set for "{veth["ifname"]}"!')
+ peer_name = veth['peer_name']
+ ifname = veth['ifname']
+
if veth['peer_name'] not in veth['other_interfaces']:
- peer_name = veth['peer_name']
- ifname = veth['ifname']
raise ConfigError(f'Used peer-name "{peer_name}" on interface "{ifname}" ' \
'is not configured!')
+ if veth['other_interfaces'][peer_name]['peer_name'] != ifname:
+ raise ConfigError(
+ f'Configuration mismatch between "{ifname}" and "{peer_name}"!')
+
+ if peer_name == ifname:
+ raise ConfigError(
+ f'Peer-name "{peer_name}" cannot be the same as interface "{ifname}"!')
+
return None
diff --git a/src/conf_mode/pki.py b/src/conf_mode/pki.py
index 29ed7b1b7..e8f3cc87a 100755
--- a/src/conf_mode/pki.py
+++ b/src/conf_mode/pki.py
@@ -16,20 +16,16 @@
from sys import exit
-import jmespath
-
from vyos.config import Config
+from vyos.configdep import set_dependents, call_dependents
from vyos.configdict import dict_merge
from vyos.configdict import node_changed
from vyos.pki import is_ca_certificate
from vyos.pki import load_certificate
-from vyos.pki import load_certificate_request
from vyos.pki import load_public_key
from vyos.pki import load_private_key
from vyos.pki import load_crl
from vyos.pki import load_dh_parameters
-from vyos.util import ask_input
-from vyos.util import call
from vyos.util import dict_search_args
from vyos.util import dict_search_recursive
from vyos.xml import defaults
@@ -121,6 +117,39 @@ def get_config(config=None):
get_first_key=True,
no_tag_node_value_mangle=True)
+ if 'changed' in pki:
+ for search in sync_search:
+ for key in search['keys']:
+ changed_key = sync_translate[key]
+
+ if changed_key not in pki['changed']:
+ continue
+
+ for item_name in pki['changed'][changed_key]:
+ node_present = False
+ if changed_key == 'openvpn':
+ node_present = dict_search_args(pki, 'openvpn', 'shared_secret', item_name)
+ else:
+ node_present = dict_search_args(pki, changed_key, item_name)
+
+ if node_present:
+ search_dict = dict_search_args(pki['system'], *search['path'])
+
+ if not search_dict:
+ continue
+
+ for found_name, found_path in dict_search_recursive(search_dict, key):
+ if found_name == item_name:
+ path = search['path']
+ path_str = ' '.join(path + found_path)
+ print(f'pki: Updating config: {path_str} {found_name}')
+
+ if path[0] == 'interfaces':
+ ifname = found_path[0]
+ set_dependents(path[1], conf, ifname)
+ else:
+ set_dependents(path[1], conf)
+
return pki
def is_valid_certificate(raw_data):
@@ -259,37 +288,7 @@ def apply(pki):
return None
if 'changed' in pki:
- for search in sync_search:
- for key in search['keys']:
- changed_key = sync_translate[key]
-
- if changed_key not in pki['changed']:
- continue
-
- for item_name in pki['changed'][changed_key]:
- node_present = False
- if changed_key == 'openvpn':
- node_present = dict_search_args(pki, 'openvpn', 'shared_secret', item_name)
- else:
- node_present = dict_search_args(pki, changed_key, item_name)
-
- if node_present:
- search_dict = dict_search_args(pki['system'], *search['path'])
-
- if not search_dict:
- continue
-
- for found_name, found_path in dict_search_recursive(search_dict, key):
- if found_name == item_name:
- path_str = ' '.join(search['path'] + found_path)
- print(f'pki: Updating config: {path_str} {found_name}')
-
- script = search['script']
- if found_path[0] == 'interfaces':
- ifname = found_path[2]
- call(f'VYOS_TAGNODE_VALUE={ifname} {script}')
- else:
- call(script)
+ call_dependents()
return None
diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py
index 5da8e7b06..73af6595b 100755
--- a/src/conf_mode/protocols_mpls.py
+++ b/src/conf_mode/protocols_mpls.py
@@ -24,6 +24,7 @@ from vyos.template import render_to_string
from vyos.util import dict_search
from vyos.util import read_file
from vyos.util import sysctl_write
+from vyos.configverify import verify_interface_exists
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -46,6 +47,10 @@ def verify(mpls):
if not mpls:
return None
+ if 'interface' in mpls:
+ for interface in mpls['interface']:
+ verify_interface_exists(interface)
+
# Checks to see if LDP is properly configured
if 'ldp' in mpls:
# If router ID not defined
diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py
index ba0249efd..600ba4e92 100755
--- a/src/conf_mode/service_pppoe-server.py
+++ b/src/conf_mode/service_pppoe-server.py
@@ -20,6 +20,7 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import get_accel_dict
+from vyos.configdict import is_node_changed
from vyos.configverify import verify_accel_ppp_base_service
from vyos.configverify import verify_interface_exists
from vyos.template import render
@@ -43,6 +44,13 @@ def get_config(config=None):
# retrieve common dictionary keys
pppoe = get_accel_dict(conf, base, pppoe_chap_secrets)
+
+ # reload-or-restart does not implemented in accel-ppp
+ # use this workaround until it will be implemented
+ # https://phabricator.accel-ppp.org/T3
+ if is_node_changed(conf, base + ['client-ip-pool']) or is_node_changed(
+ conf, base + ['client-ipv6-pool']):
+ pppoe.update({'restart_required': {}})
return pppoe
def verify(pppoe):
@@ -95,7 +103,10 @@ def apply(pppoe):
os.unlink(file)
return None
- call(f'systemctl reload-or-restart {systemd_service}')
+ if 'restart_required' in pppoe:
+ call(f'systemctl restart {systemd_service}')
+ else:
+ call(f'systemctl reload-or-restart {systemd_service}')
if __name__ == '__main__':
try:
diff --git a/src/op_mode/generate_ipsec_debug_archive.py b/src/op_mode/generate_ipsec_debug_archive.py
new file mode 100755
index 000000000..1422559a8
--- /dev/null
+++ b/src/op_mode/generate_ipsec_debug_archive.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from datetime import datetime
+from pathlib import Path
+from shutil import rmtree
+from socket import gethostname
+from sys import exit
+from tarfile import open as tar_open
+from vyos.util import rc_cmd
+
+# define a list of commands that needs to be executed
+CMD_LIST: list[str] = [
+ 'ipsec status',
+ 'swanctl -L',
+ 'swanctl -l',
+ 'swanctl -P',
+ 'ip x sa show',
+ 'ip x policy show',
+ 'ip tunnel show',
+ 'ip address',
+ 'ip rule show',
+ 'ip route | head -100',
+ 'ip route show table 220'
+]
+JOURNALCTL_CMD: str = 'journalctl -b -n 10000 /usr/lib/ipsec/charon'
+
+# execute a command and save the output to a file
+def save_stdout(command: str, file: Path) -> None:
+ rc, stdout = rc_cmd(command)
+ body: str = f'''### {command} ###
+Command: {command}
+Exit code: {rc}
+Stdout:
+{stdout}
+
+'''
+ with file.open(mode='a') as f:
+ f.write(body)
+
+
+# get local host name
+hostname: str = gethostname()
+# get current time
+time_now: str = datetime.now().isoformat(timespec='seconds')
+
+# define a temporary directory for logs and collected data
+tmp_dir: Path = Path(f'/tmp/ipsec_debug_{time_now}')
+# set file paths
+ipsec_status_file: Path = Path(f'{tmp_dir}/ipsec_status.txt')
+journalctl_charon_file: Path = Path(f'{tmp_dir}/journalctl_charon.txt')
+archive_file: str = f'/tmp/ipsec_debug_{time_now}.tar.bz2'
+
+# create files
+tmp_dir.mkdir()
+ipsec_status_file.touch()
+journalctl_charon_file.touch()
+
+try:
+ # execute all commands
+ for command in CMD_LIST:
+ save_stdout(command, ipsec_status_file)
+ save_stdout(JOURNALCTL_CMD, journalctl_charon_file)
+
+ # create an archive
+ with tar_open(name=archive_file, mode='x:bz2') as tar_file:
+ tar_file.add(tmp_dir)
+
+ # inform user about success
+ print(f'Debug file is generated and located in {archive_file}')
+except Exception as err:
+ print(f'Error during generating a debug file: {err}')
+finally:
+ # cleanup
+ rmtree(tmp_dir)
+ exit()
diff --git a/src/op_mode/generate_ipsec_debug_archive.sh b/src/op_mode/generate_ipsec_debug_archive.sh
deleted file mode 100755
index 53d0a6eaa..000000000
--- a/src/op_mode/generate_ipsec_debug_archive.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env bash
-
-# Collecting IPSec Debug Information
-
-DATE=`date +%d-%m-%Y`
-
-a_CMD=(
- "sudo ipsec status"
- "sudo swanctl -L"
- "sudo swanctl -l"
- "sudo swanctl -P"
- "sudo ip x sa show"
- "sudo ip x policy show"
- "sudo ip tunnel show"
- "sudo ip address"
- "sudo ip rule show"
- "sudo ip route"
- "sudo ip route show table 220"
- )
-
-
-echo "DEBUG: ${DATE} on host \"$(hostname)\"" > /tmp/ipsec-status-${DATE}.txt
-date >> /tmp/ipsec-status-${DATE}.txt
-
-# Execute all DEBUG commands and save it to file
-for cmd in "${a_CMD[@]}"; do
- echo -e "\n### ${cmd} ###" >> /tmp/ipsec-status-${DATE}.txt
- ${cmd} >> /tmp/ipsec-status-${DATE}.txt 2>/dev/null
-done
-
-# Collect charon logs, build .tgz archive
-sudo journalctl /usr/lib/ipsec/charon > /tmp/journalctl-charon-${DATE}.txt && \
-sudo tar -zcvf /tmp/ipsec-debug-${DATE}.tgz /tmp/journalctl-charon-${DATE}.txt /tmp/ipsec-status-${DATE}.txt >& /dev/null
-sudo rm -f /tmp/journalctl-charon-${DATE}.txt /tmp/ipsec-status-${DATE}.txt
-
-echo "Debug file is generated and located in /tmp/ipsec-debug-${DATE}.tgz"
diff --git a/src/op_mode/webproxy_update_blacklist.sh b/src/op_mode/webproxy_update_blacklist.sh
index 43a4b79fc..d5f301b75 100755
--- a/src/op_mode/webproxy_update_blacklist.sh
+++ b/src/op_mode/webproxy_update_blacklist.sh
@@ -88,7 +88,7 @@ if [[ -n $update ]] && [[ $update -eq "yes" ]]; then
# fix permissions
chown -R proxy:proxy ${db_dir}
- chmod 2770 ${db_dir}
+ chmod 755 ${db_dir}
logger --priority WARNING "webproxy blacklist entries updated (${count_before}/${count_after})"