summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/https.py29
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py8
-rwxr-xr-xsrc/etc/opennhrp/opennhrp-script.py315
-rwxr-xr-xsrc/op_mode/conntrack.py23
-rwxr-xr-xsrc/system/keepalived-fifo.py12
5 files changed, 319 insertions, 68 deletions
diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py
index 3057357fc..7cd7ea42e 100755
--- a/src/conf_mode/https.py
+++ b/src/conf_mode/https.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2021 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -29,6 +29,8 @@ from vyos.pki import wrap_certificate
from vyos.pki import wrap_private_key
from vyos.template import render
from vyos.util import call
+from vyos.util import check_port_availability
+from vyos.util import is_listen_port_bind_service
from vyos.util import write_file
from vyos import airbag
@@ -107,6 +109,31 @@ def verify(https):
raise ConfigError("At least one 'virtual-host <id> server-name' "
"matching the 'certbot domain-name' is required.")
+ server_block_list = []
+
+ # organize by vhosts
+ vhost_dict = https.get('virtual-host', {})
+
+ if not vhost_dict:
+ # no specified virtual hosts (server blocks); use default
+ server_block_list.append(default_server_block)
+ else:
+ for vhost in list(vhost_dict):
+ server_block = deepcopy(default_server_block)
+ data = vhost_dict.get(vhost, {})
+ server_block['address'] = data.get('listen-address', '*')
+ server_block['port'] = data.get('listen-port', '443')
+ server_block_list.append(server_block)
+
+ for entry in server_block_list:
+ _address = entry.get('address')
+ _address = '0.0.0.0' if _address == '*' else _address
+ _port = entry.get('port')
+ proto = 'tcp'
+ if check_port_availability(_address, int(_port), proto) is not True and \
+ not is_listen_port_bind_service(int(_port), 'nginx'):
+ raise ConfigError(f'"{proto}" port "{_port}" is used by another service')
+
verify_vrf(https)
return None
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py
index bad9cfbd8..5ca32d23e 100755
--- a/src/conf_mode/vpn_ipsec.py
+++ b/src/conf_mode/vpn_ipsec.py
@@ -595,13 +595,11 @@ def wait_for_vici_socket(timeout=5, sleep_interval=0.1):
sleep(sleep_interval)
def apply(ipsec):
+ systemd_service = 'strongswan-starter.service'
if not ipsec:
- call('sudo ipsec stop')
+ call(f'systemctl stop {systemd_service}')
else:
- call('sudo ipsec restart')
- call('sudo ipsec rereadall')
- call('sudo ipsec reload')
-
+ call(f'systemctl reload-or-restart {systemd_service}')
if wait_for_vici_socket():
call('sudo swanctl -q')
diff --git a/src/etc/opennhrp/opennhrp-script.py b/src/etc/opennhrp/opennhrp-script.py
index 8274e6564..a5293c97e 100755
--- a/src/etc/opennhrp/opennhrp-script.py
+++ b/src/etc/opennhrp/opennhrp-script.py
@@ -18,44 +18,120 @@ import os
import re
import sys
import vici
+
from json import loads
+from pathlib import Path
+from vyos.logger import getLogger
from vyos.util import cmd
from vyos.util import process_named_running
-NHRP_CONFIG = "/run/opennhrp/opennhrp.conf"
+NHRP_CONFIG: str = '/run/opennhrp/opennhrp.conf'
+
+
+def vici_get_ipsec_uniqueid(conn: str, src_nbma: str,
+ dst_nbma: str) -> list[str]:
+ """ Find and return IKE SAs by src nbma and dst nbma
+
+ Args:
+ conn (str): a connection name
+ src_nbma (str): an IP address of NBMA source
+ dst_nbma (str): an IP address of NBMA destination
+
+ Returns:
+ list: a list of IKE connections that match a criteria
+ """
+ if not conn or not src_nbma or not dst_nbma:
+ logger.error(
+ f'Incomplete input data for resolving IKE unique ids: '
+ f'conn: {conn}, src_nbma: {src_nbma}, dst_nbma: {dst_nbma}')
+ return []
+
+ try:
+ logger.info(
+ f'Resolving IKE unique ids for: conn: {conn}, '
+ f'src_nbma: {src_nbma}, dst_nbma: {dst_nbma}')
+ session: vici.Session = vici.Session()
+ list_ikeid: list[str] = []
+ list_sa = session.list_sas({'ike': conn})
+ for sa in list_sa:
+ if sa[conn]['local-host'].decode('ascii') == src_nbma \
+ and sa[conn]['remote-host'].decode('ascii') == dst_nbma:
+ list_ikeid.append(sa[conn]['uniqueid'].decode('ascii'))
+ return list_ikeid
+ except Exception as err:
+ logger.error(f'Unable to find unique ids for IKE: {err}')
+ return []
+
+
+def vici_ike_terminate(list_ikeid: list[str]) -> bool:
+ """Terminating IKE SAs by list of IKE IDs
+
+ Args:
+ list_ikeid (list[str]): a list of IKE ids to terminate
+
+ Returns:
+ bool: result of termination action
+ """
+ if not list:
+ logger.warning('An empty list for termination was provided')
+ return False
+
+ try:
+ session = vici.Session()
+ for ikeid in list_ikeid:
+ logger.info(f'Terminating IKE SA with id {ikeid}')
+ session.terminate({'ike-id': ikeid, 'timeout': '-1'})
+ return True
+ except Exception as err:
+ logger.error(f'Failed to terminate SA for IKE ids {list_ikeid}: {err}')
+ return False
+
+def parse_type_ipsec(interface: str) -> tuple[str, str]:
+ """Get DMVPN Type and NHRP Profile from the configuration
-def parse_type_ipsec(interface):
- with open(NHRP_CONFIG, 'r') as f:
- lines = f.readlines()
- match = rf'^interface {interface} #(hub|spoke)(?:\s([\w-]+))?$'
- for line in lines:
- m = re.match(match, line)
- if m:
- return m[1], m[2]
- return None, None
+ Args:
+ interface (str): a name of interface
+
+ Returns:
+ tuple[str, str]: `peer_type` and `profile_name`
+ """
+ if not interface:
+ logger.error('Cannot find peer type - no input provided')
+ return '', ''
+
+ config_file: str = Path(NHRP_CONFIG).read_text()
+ regex: str = rf'^interface {interface} #(?P<peer_type>hub|spoke) ?(?P<profile_name>[^\n]*)$'
+ match = re.search(regex, config_file, re.M)
+ if match:
+ return match.groupdict()['peer_type'], match.groupdict()[
+ 'profile_name']
+ return '', ''
def add_peer_route(nbma_src: str, nbma_dst: str, mtu: str) -> None:
"""Add a route to a NBMA peer
Args:
- nmba_src (str): a local IP address
+ nbma_src (str): a local IP address
nbma_dst (str): a remote IP address
mtu (str): a MTU for a route
"""
+ logger.info(f'Adding route from {nbma_src} to {nbma_dst} with MTU {mtu}')
# Find routes to a peer
- route_get_cmd = f'sudo ip -j route get {nbma_dst} from {nbma_src}'
+ route_get_cmd: str = f'sudo ip --json route get {nbma_dst} from {nbma_src}'
try:
route_info_data = loads(cmd(route_get_cmd))
except Exception as err:
- print(f'Unable to find a route to {nbma_dst}: {err}')
+ logger.error(f'Unable to find a route to {nbma_dst}: {err}')
+ return
# Check if an output has an expected format
if not isinstance(route_info_data, list):
- print(f'Garbage returned from the "{route_get_cmd}" command: \
- {route_info_data}')
+ logger.error(
+ f'Garbage returned from the "{route_get_cmd}" '
+ f'command: {route_info_data}')
return
# Add static routes to a peer
@@ -76,104 +152,217 @@ def add_peer_route(nbma_src: str, nbma_dst: str, mtu: str) -> None:
try:
cmd(route_add_cmd)
except Exception as err:
- print(f'Unable to add a route using command "{route_add_cmd}": \
- {err}')
+ logger.error(
+ f'Unable to add a route using command "{route_add_cmd}": '
+ f'{err}')
-def vici_initiate(conn, child_sa, src_addr, dest_addr):
- try:
- session = vici.Session()
- logs = session.initiate({
- 'ike': conn,
- 'child': child_sa,
- 'timeout': '-1',
- 'my-host': src_addr,
- 'other-host': dest_addr
- })
- for log in logs:
- message = log['msg'].decode('ascii')
- print('INIT LOG:', message)
- return True
- except:
- return None
+def vici_initiate(conn: str, child_sa: str, src_addr: str,
+ dest_addr: str) -> bool:
+ """Initiate IKE SA connection with specific peer
+ Args:
+ conn (str): an IKE connection name
+ child_sa (str): a child SA profile name
+ src_addr (str): NBMA local address
+ dest_addr (str): NBMA address of a peer
-def vici_terminate(conn, child_sa, src_addr, dest_addr):
+ Returns:
+ bool: a result of initiation command
+ """
+ logger.info(
+ f'Trying to initiate connection. Name: {conn}, child sa: {child_sa}, '
+ f'src_addr: {src_addr}, dst_addr: {dest_addr}')
try:
session = vici.Session()
- logs = session.terminate({
+ session.initiate({
'ike': conn,
'child': child_sa,
'timeout': '-1',
'my-host': src_addr,
'other-host': dest_addr
})
- for log in logs:
- message = log['msg'].decode('ascii')
- print('TERM LOG:', message)
return True
- except:
- return None
+ except Exception as err:
+ logger.error(f'Unable to initiate connection {err}')
+ return False
+
+
+def vici_terminate(conn: str, src_addr: str, dest_addr: str) -> None:
+ """Find and terminate IKE SAs by local NBMA and remote NBMA addresses
+
+ Args:
+ conn (str): IKE connection name
+ src_addr (str): NBMA local address
+ dest_addr (str): NBMA address of a peer
+ """
+ logger.info(
+ f'Terminating IKE connection {conn} between {src_addr} '
+ f'and {dest_addr}')
+ ikeid_list: list[str] = vici_get_ipsec_uniqueid(conn, src_addr, dest_addr)
-def iface_up(interface):
- cmd(f'sudo ip route flush proto 42 dev {interface}')
- cmd(f'sudo ip neigh flush dev {interface}')
+ if not ikeid_list:
+ logger.warning(
+ f'No active sessions found for IKE profile {conn}, '
+ f'local NBMA {src_addr}, remote NBMA {dest_addr}')
+ else:
+ vici_ike_terminate(ikeid_list)
-def peer_up(dmvpn_type, conn):
- # src_addr = os.getenv('NHRP_SRCADDR')
+def iface_up(interface: str) -> None:
+ """Proceed tunnel interface UP event
+
+ Args:
+ interface (str): an interface name
+ """
+ if not interface:
+ logger.warning('No interface name provided for UP event')
+
+ logger.info(f'Turning up interface {interface}')
+ try:
+ cmd(f'sudo ip route flush proto 42 dev {interface}')
+ cmd(f'sudo ip neigh flush dev {interface}')
+ except Exception as err:
+ logger.error(
+ f'Unable to flush route on interface "{interface}": {err}')
+
+
+def peer_up(dmvpn_type: str, conn: str) -> None:
+ """Proceed NHRP peer UP event
+
+ Args:
+ dmvpn_type (str): a type of peer
+ conn (str): an IKE profile name
+ """
+ logger.info(f'Peer UP event for {dmvpn_type} using IKE profile {conn}')
src_nbma = os.getenv('NHRP_SRCNBMA')
- # dest_addr = os.getenv('NHRP_DESTADDR')
dest_nbma = os.getenv('NHRP_DESTNBMA')
dest_mtu = os.getenv('NHRP_DESTMTU')
+ if not src_nbma or not dest_nbma:
+ logger.error(
+ f'Can not get NHRP NBMA addresses: local {src_nbma}, '
+ f'remote {dest_nbma}')
+ return
+
+ logger.info(f'NBMA addresses: local {src_nbma}, remote {dest_nbma}')
if dest_mtu:
add_peer_route(src_nbma, dest_nbma, dest_mtu)
-
if conn and dmvpn_type == 'spoke' and process_named_running('charon'):
- vici_terminate(conn, 'dmvpn', src_nbma, dest_nbma)
+ vici_terminate(conn, src_nbma, dest_nbma)
vici_initiate(conn, 'dmvpn', src_nbma, dest_nbma)
-def peer_down(dmvpn_type, conn):
+def peer_down(dmvpn_type: str, conn: str) -> None:
+ """Proceed NHRP peer DOWN event
+
+ Args:
+ dmvpn_type (str): a type of peer
+ conn (str): an IKE profile name
+ """
+ logger.info(f'Peer DOWN event for {dmvpn_type} using IKE profile {conn}')
+
src_nbma = os.getenv('NHRP_SRCNBMA')
dest_nbma = os.getenv('NHRP_DESTNBMA')
+ if not src_nbma or not dest_nbma:
+ logger.error(
+ f'Can not get NHRP NBMA addresses: local {src_nbma}, '
+ f'remote {dest_nbma}')
+ return
+
+ logger.info(f'NBMA addresses: local {src_nbma}, remote {dest_nbma}')
if conn and dmvpn_type == 'spoke' and process_named_running('charon'):
- vici_terminate(conn, 'dmvpn', src_nbma, dest_nbma)
+ vici_terminate(conn, src_nbma, dest_nbma)
+ try:
+ cmd(f'sudo ip route del {dest_nbma} src {src_nbma} proto 42')
+ except Exception as err:
+ logger.error(
+ f'Unable to del route from {src_nbma} to {dest_nbma}: {err}')
- cmd(f'sudo ip route del {dest_nbma} src {src_nbma} proto 42')
+def route_up(interface: str) -> None:
+ """Proceed NHRP route UP event
+
+ Args:
+ interface (str): an interface name
+ """
+ logger.info(f'Route UP event for interface {interface}')
-def route_up(interface):
dest_addr = os.getenv('NHRP_DESTADDR')
dest_prefix = os.getenv('NHRP_DESTPREFIX')
next_hop = os.getenv('NHRP_NEXTHOP')
- cmd(f'sudo ip route replace {dest_addr}/{dest_prefix} proto 42 \
- via {next_hop} dev {interface}')
- cmd('sudo ip route flush cache')
+ if not dest_addr or not dest_prefix or not next_hop:
+ logger.error(
+ f'Can not get route details: dest_addr {dest_addr}, '
+ f'dest_prefix {dest_prefix}, next_hop {next_hop}')
+ return
+
+ logger.info(
+ f'Route details: dest_addr {dest_addr}, dest_prefix {dest_prefix}, '
+ f'next_hop {next_hop}')
+ try:
+ cmd(f'sudo ip route replace {dest_addr}/{dest_prefix} proto 42 \
+ via {next_hop} dev {interface}')
+ cmd('sudo ip route flush cache')
+ except Exception as err:
+ logger.error(
+ f'Unable replace or flush route to {dest_addr}/{dest_prefix} '
+ f'via {next_hop} dev {interface}: {err}')
+
+
+def route_down(interface: str) -> None:
+ """Proceed NHRP route DOWN event
+
+ Args:
+ interface (str): an interface name
+ """
+ logger.info(f'Route DOWN event for interface {interface}')
-def route_down(interface):
dest_addr = os.getenv('NHRP_DESTADDR')
dest_prefix = os.getenv('NHRP_DESTPREFIX')
- cmd(f'sudo ip route del {dest_addr}/{dest_prefix} proto 42')
- cmd('sudo ip route flush cache')
+ if not dest_addr or not dest_prefix:
+ logger.error(
+ f'Can not get route details: dest_addr {dest_addr}, '
+ f'dest_prefix {dest_prefix}')
+ return
+
+ logger.info(
+ f'Route details: dest_addr {dest_addr}, dest_prefix {dest_prefix}')
+ try:
+ cmd(f'sudo ip route del {dest_addr}/{dest_prefix} proto 42')
+ cmd('sudo ip route flush cache')
+ except Exception as err:
+ logger.error(
+ f'Unable delete or flush route to {dest_addr}/{dest_prefix}: '
+ f'{err}')
if __name__ == '__main__':
+ logger = getLogger('opennhrp-script', syslog=True)
+ logger.debug(
+ f'Running script with arguments: {sys.argv}, '
+ f'environment: {os.environ}')
+
action = sys.argv[1]
interface = os.getenv('NHRP_INTERFACE')
- dmvpn_type, profile_name = parse_type_ipsec(interface)
- dmvpn_conn = None
+ if not interface:
+ logger.error('Can not get NHRP interface name')
+ sys.exit(1)
- if profile_name:
- dmvpn_conn = f'dmvpn-{profile_name}-{interface}'
+ dmvpn_type, profile_name = parse_type_ipsec(interface)
+ if not dmvpn_type:
+ logger.info(f'Interface {interface} is not NHRP tunnel')
+ sys.exit()
+ dmvpn_conn: str = ''
+ if profile_name:
+ dmvpn_conn: str = f'dmvpn-{profile_name}-{interface}'
if action == 'interface-up':
iface_up(interface)
elif action == 'peer-register':
@@ -186,3 +375,5 @@ if __name__ == '__main__':
route_up(interface)
elif action == 'route-down':
route_down(interface)
+
+ sys.exit()
diff --git a/src/op_mode/conntrack.py b/src/op_mode/conntrack.py
index 036226418..b27aa6060 100755
--- a/src/op_mode/conntrack.py
+++ b/src/op_mode/conntrack.py
@@ -51,6 +51,21 @@ def _get_raw_data(family):
return _xml_to_dict(xml)
+def _get_raw_statistics():
+ entries = []
+ data = cmd('sudo conntrack -S')
+ data = data.replace(' \t', '').split('\n')
+ for entry in data:
+ entries.append(entry.split())
+ return entries
+
+
+def get_formatted_statistics(entries):
+ headers = ["CPU", "Found", "Invalid", "Insert", "Insert fail", "Drop", "Early drop", "Errors", "Search restart"]
+ output = tabulate(entries, headers, numalign="left")
+ return output
+
+
def get_formatted_output(dict_data):
"""
:param xml:
@@ -111,6 +126,14 @@ def show(raw: bool, family: str):
return get_formatted_output(conntrack_data)
+def show_statistics(raw: bool):
+ conntrack_statistics = _get_raw_statistics()
+ if raw:
+ return conntrack_statistics
+ else:
+ return get_formatted_statistics(conntrack_statistics)
+
+
if __name__ == '__main__':
try:
res = vyos.opmode.run(sys.modules[__name__])
diff --git a/src/system/keepalived-fifo.py b/src/system/keepalived-fifo.py
index a8df232ae..a0fccd1d0 100755
--- a/src/system/keepalived-fifo.py
+++ b/src/system/keepalived-fifo.py
@@ -30,6 +30,7 @@ from vyos.ifconfig.vrrp import VRRP
from vyos.configquery import ConfigTreeQuery
from vyos.util import cmd
from vyos.util import dict_search
+from vyos.util import commit_in_progress
# configure logging
logger = logging.getLogger(__name__)
@@ -63,6 +64,17 @@ class KeepalivedFifo:
# load configuration
def _config_load(self):
+ # For VRRP configuration to be read, the commit must be finished
+ count = 1
+ while commit_in_progress():
+ if ( count <= 40 ):
+ logger.debug(f'commit in progress try: {count}')
+ else:
+ logger.error(f'commit still in progress after {count} continuing anyway')
+ break
+ count += 1
+ time.sleep(0.5)
+
try:
base = ['high-availability', 'vrrp']
conf = ConfigTreeQuery()