diff options
| -rw-r--r-- | data/templates/ipsec/ipsec.conf.j2 | 19 | ||||
| -rw-r--r-- | data/templates/ipsec/ipsec.secrets.j2 | 5 | ||||
| -rw-r--r-- | debian/control | 1 | ||||
| -rw-r--r-- | interface-definitions/include/interface/authentication.xml.i | 6 | ||||
| -rw-r--r-- | interface-definitions/include/version/ipsec-version.xml.i | 2 | ||||
| -rw-r--r-- | interface-definitions/vpn-ipsec.xml.in | 12 | ||||
| -rw-r--r-- | op-mode-definitions/monitor-log.xml.in | 4 | ||||
| -rw-r--r-- | op-mode-definitions/show-log.xml.in | 4 | ||||
| -rw-r--r-- | op-mode-definitions/vpn-ipsec.xml.in | 14 | ||||
| -rw-r--r-- | python/vyos/config_mgmt.py | 43 | ||||
| -rw-r--r-- | python/vyos/configtree.py | 32 | ||||
| -rw-r--r-- | python/vyos/configverify.py | 11 | ||||
| -rwxr-xr-x | src/conf_mode/vpn_ipsec.py | 19 | ||||
| -rwxr-xr-x | src/migration-scripts/ipsec/11-to-12 | 53 | ||||
| -rwxr-xr-x | src/op_mode/ipsec.py | 2 | ||||
| -rwxr-xr-x | src/services/vyos-http-api-server | 4 | 
16 files changed, 125 insertions, 106 deletions
diff --git a/data/templates/ipsec/ipsec.conf.j2 b/data/templates/ipsec/ipsec.conf.j2 deleted file mode 100644 index f63995b38..000000000 --- a/data/templates/ipsec/ipsec.conf.j2 +++ /dev/null @@ -1,19 +0,0 @@ -# Created by VyOS - manual changes will be overwritten - -config setup -{% set charondebug = '' %} -{% if log.subsystem is vyos_defined %} -{%     set subsystem = log.subsystem %} -{%     if 'any' in log.subsystem %} -{%         set subsystem = ['dmn', 'mgr', 'ike', 'chd','job', 'cfg', 'knl', -                            'net', 'asn', 'enc', 'lib', 'esp', 'tls', 'tnc', -                            'imc', 'imv', 'pts'] %} -{%     endif %} -{%     set charondebug = subsystem | join (' ' ~ log.level ~ ', ') ~ ' ' ~ log.level %} -{% endif %} -    charondebug = "{{ charondebug }}" -    uniqueids = {{ "no" if disable_uniqreqids is vyos_defined else "yes" }} - -{% if include_ipsec_conf is vyos_defined %} -include {{ include_ipsec_conf }} -{% endif %} diff --git a/data/templates/ipsec/ipsec.secrets.j2 b/data/templates/ipsec/ipsec.secrets.j2 deleted file mode 100644 index a87ac9bc7..000000000 --- a/data/templates/ipsec/ipsec.secrets.j2 +++ /dev/null @@ -1,5 +0,0 @@ -# Created by VyOS - manual changes will be overwritten - -{% if include_ipsec_secrets is vyos_defined %} -include {{ include_ipsec_secrets }} -{% endif %} diff --git a/debian/control b/debian/control index 19f98b799..fb29697d4 100644 --- a/debian/control +++ b/debian/control @@ -40,6 +40,7 @@ Depends:    beep,    bmon,    bsdmainutils, +  charon-systemd,    conntrack,    conntrackd,    conserver-client, diff --git a/interface-definitions/include/interface/authentication.xml.i b/interface-definitions/include/interface/authentication.xml.i index 8bb094da7..ac06faef5 100644 --- a/interface-definitions/include/interface/authentication.xml.i +++ b/interface-definitions/include/interface/authentication.xml.i @@ -12,9 +12,9 @@            <description>Username</description>          </valueHelp>          <constraint> -          <regex>[[:alnum:]][-_#@[:alnum:]]{0,127}</regex> +          <regex>[[:ascii:]]{1,128}</regex>          </constraint> -        <constraintErrorMessage>Username is limited to alphanumerical characters, -, _, #, and @ with a total lenght of 128</constraintErrorMessage> +        <constraintErrorMessage>Username is limited to ASCII characters only, with a total length of 128</constraintErrorMessage>        </properties>      </leafNode>      <leafNode name="password"> @@ -27,7 +27,7 @@          <constraint>            <regex>[[:ascii:]]{1,128}</regex>          </constraint> -        <constraintErrorMessage>Password is limited to ASCII characters only, with a total lenght of 128</constraintErrorMessage> +        <constraintErrorMessage>Password is limited to ASCII characters only, with a total length of 128</constraintErrorMessage>        </properties>      </leafNode>    </children> diff --git a/interface-definitions/include/version/ipsec-version.xml.i b/interface-definitions/include/version/ipsec-version.xml.i index 8d019b466..de7a9c088 100644 --- a/interface-definitions/include/version/ipsec-version.xml.i +++ b/interface-definitions/include/version/ipsec-version.xml.i @@ -1,3 +1,3 @@  <!-- include start from include/version/ipsec-version.xml.i --> -<syntaxVersion component='ipsec' version='11'></syntaxVersion> +<syntaxVersion component='ipsec' version='12'></syntaxVersion>  <!-- include end --> diff --git a/interface-definitions/vpn-ipsec.xml.in b/interface-definitions/vpn-ipsec.xml.in index 9d20926ec..1b3a5532e 100644 --- a/interface-definitions/vpn-ipsec.xml.in +++ b/interface-definitions/vpn-ipsec.xml.in @@ -269,6 +269,7 @@                      <regex>(none|hold|restart)</regex>                    </constraint>                  </properties> +                <defaultValue>none</defaultValue>                </leafNode>                <node name="dead-peer-detection">                  <properties> @@ -297,6 +298,7 @@                          <regex>(hold|clear|restart)</regex>                        </constraint>                      </properties> +                    <defaultValue>clear</defaultValue>                    </leafNode>                    <leafNode name="interval">                      <properties> @@ -544,16 +546,6 @@                </tagNode>              </children>            </tagNode> -          <leafNode name="include-ipsec-conf"> -            <properties> -              <help>Absolute path to specify a strongSwan config include file</help> -            </properties> -          </leafNode> -          <leafNode name="include-ipsec-secrets"> -            <properties> -              <help>Absolute path to a strongSwan secrets include file</help> -            </properties> -          </leafNode>            #include <include/generic-interface-multi.xml.i>            <node name="log">              <properties> diff --git a/op-mode-definitions/monitor-log.xml.in b/op-mode-definitions/monitor-log.xml.in index ec428a676..d5892398b 100644 --- a/op-mode-definitions/monitor-log.xml.in +++ b/op-mode-definitions/monitor-log.xml.in @@ -274,13 +274,13 @@                  <properties>                    <help>Monitor last lines of ALL VPNs</help>                  </properties> -                <command>journalctl --no-hostname --boot --follow --unit strongswan-starter.service --unit accel-ppp@*.service --unit ocserv.service</command> +                <command>journalctl --no-hostname --boot --follow --unit strongswan.service --unit accel-ppp@*.service --unit ocserv.service</command>                </leafNode>                <leafNode name="ipsec">                  <properties>                    <help>Monitor last lines of IPsec</help>                  </properties> -                <command>journalctl --no-hostname --boot --follow --unit strongswan-starter.service</command> +                <command>journalctl --no-hostname --boot --follow --unit strongswan.service</command>                </leafNode>                <leafNode name="l2tp">                  <properties> diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in index f5e5b1493..c626e45fb 100644 --- a/op-mode-definitions/show-log.xml.in +++ b/op-mode-definitions/show-log.xml.in @@ -421,13 +421,13 @@                  <properties>                    <help>Show log for ALL</help>                  </properties> -                <command>journalctl --no-hostname --boot --unit strongswan-starter.service --unit accel-ppp@*.service --unit ocserv.service</command> +                <command>journalctl --no-hostname --boot --unit strongswan.service --unit accel-ppp@*.service --unit ocserv.service</command>                </leafNode>                <leafNode name="ipsec">                  <properties>                    <help>Show log for IPsec</help>                  </properties> -                <command>journalctl --no-hostname --boot --unit strongswan-starter.service</command> +                <command>journalctl --no-hostname --boot --unit strongswan.service</command>                </leafNode>                <leafNode name="l2tp">                  <properties> diff --git a/op-mode-definitions/vpn-ipsec.xml.in b/op-mode-definitions/vpn-ipsec.xml.in index 803ce4cc2..eca9f6fd9 100644 --- a/op-mode-definitions/vpn-ipsec.xml.in +++ b/op-mode-definitions/vpn-ipsec.xml.in @@ -28,7 +28,7 @@                  <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_peer --peer="$4" --tunnel="vti"</command>                </node>              </children> -            <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_peer --peer="$4" --tunnel="all"</command> +            <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_peer --peer="$4"</command>            </tagNode>            <tagNode name="ipsec-profile">              <properties> @@ -53,11 +53,11 @@    </node>    <node name="restart">      <children> -      <node name="vpn"> +      <node name="ipsec">          <properties>            <help>Restart the IPsec VPN process</help>          </properties> -        <command>if pgrep charon >/dev/null ; then sudo ipsec restart ; sleep 3 ; sudo swanctl -q ; else echo "IPsec process not running" ; fi</command> +        <command>if systemctl is-active --quiet strongswan; then sudo systemctl restart strongswan ; echo "IPsec process restarted";  else echo "IPsec process not running" ; fi</command>        </node>      </children>    </node> @@ -128,7 +128,7 @@                  <properties>                    <help>Show summary of IKE process information</help>                  </properties> -                <command>if pgrep charon >/dev/null ; then echo "Running: $(pgrep charon)" ; else echo "Process is not running" ; fi</command> +                <command>if systemctl is-active --quiet strongswan ; then systemctl status strongswan ; else echo "Process is not running" ; fi</command>                </node>              </children>            </node> @@ -190,10 +190,10 @@                      <properties>                        <help>Show Verbose Detail on all active IPsec Security Associations (SA)</help>                      </properties> -                    <command>if pgrep charon >/dev/null ; then sudo /usr/sbin/ipsec statusall ; else echo "IPsec process not running" ; fi</command> +                    <command>if systemctl is-active --quiet strongswan ; then sudo /usr/sbin/ipsec statusall ; else echo "IPsec process not running" ; fi</command>                    </node>                  </children> -                <command>if pgrep charon >/dev/null ; then sudo ${vyos_op_scripts_dir}/ipsec.py show_sa ; else echo "IPsec process not running" ; fi</command> +                <command>if systemctl is-active --quiet strongswan ; then sudo ${vyos_op_scripts_dir}/ipsec.py show_sa ; else echo "IPsec process not running" ; fi</command>                </node>                <node name="state">                  <properties> @@ -205,7 +205,7 @@                  <properties>                    <help>Show status of IPsec process</help>                  </properties> -                <command>if pgrep charon >/dev/null ; then echo -e "IPsec Process Running: $(pgrep charon)\n$(sudo /usr/sbin/ipsec status)" ; else echo "IPsec process not running" ; fi</command> +                <command>if systemctl is-active --quiet strongswan >/dev/null ; then echo -e "IPsec Process Running: $(pgrep charon)\n$(sudo /usr/sbin/ipsec status)" ; else echo "IPsec process not running" ; fi</command>                </node>              </children>            </node> diff --git a/python/vyos/config_mgmt.py b/python/vyos/config_mgmt.py index 22a49ff50..fade3081c 100644 --- a/python/vyos/config_mgmt.py +++ b/python/vyos/config_mgmt.py @@ -24,7 +24,7 @@ from datetime import datetime  from tabulate import tabulate  from vyos.config import Config -from vyos.configtree import ConfigTree +from vyos.configtree import ConfigTree, ConfigTreeError, show_diff  from vyos.defaults import directories  from vyos.util import is_systemd_service_active, ask_yes_no, rc_cmd @@ -93,15 +93,7 @@ class ConfigMgmt:          # a call to compare without args is edit_level aware          edit_level = os.getenv('VYATTA_EDIT_LEVEL', '') -        edit_path = [l for l in edit_level.split('/') if l] -        if edit_path: -            eff_conf = config.show_config(edit_path, effective=True) -            self.edit_level_active_config = ConfigTree(eff_conf) -            conf = config.show_config(edit_path) -            self.edit_level_working_config = ConfigTree(conf) -        else: -            self.edit_level_active_config = None -            self.edit_level_working_config = None +        self.edit_path = [l for l in edit_level.split('/') if l]          self.active_config = config._running_config          self.working_config = config._session_config @@ -241,14 +233,8 @@ Proceed ?'''          revision n vs. revision m; working version vs. active version;          or working version vs. saved version.          """ -        from difflib import unified_diff - -        ct1 = self.edit_level_active_config -        if ct1 is None: -            ct1 = self.active_config -        ct2 = self.edit_level_working_config -        if ct2 is None: -            ct2 = self.working_config +        ct1 = self.active_config +        ct2 = self.working_config          msg = 'No changes between working and active configurations.\n'          if saved:              ct1 = self._get_saved_config_tree() @@ -268,19 +254,16 @@ Proceed ?'''              ct1 = self._get_config_tree_revision(rev2)              msg = f'No changes between revisions {rev2} and {rev1} configurations.\n' -        if commands: -            lines1 = ct1.to_commands().splitlines(keepends=True) -            lines2 = ct2.to_commands().splitlines(keepends=True) -        else: -            lines1 = ct1.to_string().splitlines(keepends=True) -            lines2 = ct2.to_string().splitlines(keepends=True) -          out = '' -        comp = unified_diff(lines1, lines2) -        for line in comp: -            if re.match(r'(\-\-)|(\+\+)|(@@)', line): -                continue -            out += line +        path = [] if commands else self.edit_path +        try: +            if commands: +                out = show_diff(ct1, ct2, path=path, commands=True) +            else: +                out = show_diff(ct1, ct2, path=path) +        except ConfigTreeError as e: +            return e, 1 +          if out:              msg = out diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py index f2358ee4f..c0b3ebd78 100644 --- a/python/vyos/configtree.py +++ b/python/vyos/configtree.py @@ -16,7 +16,7 @@ import os  import re  import json -from ctypes import cdll, c_char_p, c_void_p, c_int +from ctypes import cdll, c_char_p, c_void_p, c_int, c_bool  LIBPATH = '/usr/lib/libvyosconfig.so.0' @@ -322,6 +322,36 @@ class ConfigTree(object):          subt = ConfigTree(address=res)          return subt +def show_diff(left, right, path=[], commands=False, libpath=LIBPATH): +    if left is None: +        left = ConfigTree(config_string='\n') +    if right is None: +        right = ConfigTree(config_string='\n') +    if not (isinstance(left, ConfigTree) and isinstance(right, ConfigTree)): +        raise TypeError("Arguments must be instances of ConfigTree") +    if path: +        if (not left.exists(path)) and (not right.exists(path)): +            raise ConfigTreeError(f"Path {path} doesn't exist") + +    check_path(path) +    path_str = " ".join(map(str, path)).encode() + +    __lib = cdll.LoadLibrary(libpath) +    __show_diff = __lib.show_diff +    __show_diff.argtypes = [c_bool, c_char_p, c_void_p, c_void_p] +    __show_diff.restype = c_char_p +    __get_error = __lib.get_error +    __get_error.argtypes = [] +    __get_error.restype = c_char_p + +    res = __show_diff(commands, path_str, left._get_config(), right._get_config()) +    res = res.decode() +    if res == "#1@": +        msg = __get_error().decode() +        raise ConfigTreeError(msg) + +    return res +  class DiffTree:      def __init__(self, left, right, path=[], libpath=LIBPATH):          if left is None: diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 30bdd2d47..fcc8cc733 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -420,11 +420,12 @@ def verify_accel_ppp_base_service(config, local_users=True):      if 'gateway_address' in config:          gateway = True      else: -        if dict_search_recursive(config, 'gateway_address', ['client_ip_pool', 'name']): -            for _, v in config['client_ip_pool']['name'].items(): -                if 'gateway_address' in v: -                    gateway = True -                    break +        if 'client_ip_pool' in config: +            if dict_search_recursive(config, 'gateway_address', ['client_ip_pool', 'name']): +                for _, v in config['client_ip_pool']['name'].items(): +                    if 'gateway_address' in v: +                        gateway = True +                        break      if not gateway:          raise ConfigError('Server requires gateway-address to be configured!') diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index ce4f13d27..8263358ea 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -53,8 +53,6 @@ dhcp_wait_attempts = 2  dhcp_wait_sleep = 1  swanctl_dir        = '/etc/swanctl' -ipsec_conf         = '/etc/ipsec.conf' -ipsec_secrets      = '/etc/ipsec.secrets'  charon_conf        = '/etc/strongswan.d/charon.conf'  charon_dhcp_conf   = '/etc/strongswan.d/charon/dhcp.conf'  charon_radius_conf = '/etc/strongswan.d/charon/eap-radius.conf' @@ -618,8 +616,6 @@ def generate(ipsec):                          if id:                              ipsec['authentication']['psk'][psk]['id'].append(id) -    render(ipsec_conf, 'ipsec/ipsec.conf.j2', ipsec) -    render(ipsec_secrets, 'ipsec/ipsec.secrets.j2', ipsec)      render(charon_conf, 'ipsec/charon.j2', ipsec)      render(charon_dhcp_conf, 'ipsec/charon/dhcp.conf.j2', ipsec)      render(charon_radius_conf, 'ipsec/charon/eap-radius.conf.j2', ipsec) @@ -634,25 +630,12 @@ def resync_nhrp(ipsec):      if tmp > 0:          print('ERROR: failed to reapply NHRP settings!') -def wait_for_vici_socket(timeout=5, sleep_interval=0.1): -    start_time = time() -    test_command = f'sudo socat -u OPEN:/dev/null UNIX-CONNECT:{vici_socket}' -    while True: -        if (start_time + timeout) < time(): -            return None -        result = run(test_command) -        if result == 0: -            return True -        sleep(sleep_interval) -  def apply(ipsec): -    systemd_service = 'strongswan-starter.service' +    systemd_service = 'strongswan.service'      if not ipsec:          call(f'systemctl stop {systemd_service}')      else:          call(f'systemctl reload-or-restart {systemd_service}') -        if wait_for_vici_socket(): -            call('sudo swanctl -q')      resync_nhrp(ipsec) diff --git a/src/migration-scripts/ipsec/11-to-12 b/src/migration-scripts/ipsec/11-to-12 new file mode 100755 index 000000000..8bbde5efa --- /dev/null +++ b/src/migration-scripts/ipsec/11-to-12 @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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/>. + +# Remove legacy ipsec.conf and ipsec.secrets - Not supported with swanctl + +import re + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree + +if (len(argv) < 1): +    print("Must specify file name!") +    exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: +    config_file = f.read() + +base = ['vpn', 'ipsec'] +config = ConfigTree(config_file) + +if not config.exists(base): +    # Nothing to do +    exit(0) + +if config.exists(base + ['include-ipsec-conf']): +    config.delete(base + ['include-ipsec-conf']) + +if config.exists(base + ['include-ipsec-secrets']): +    config.delete(base + ['include-ipsec-secrets']) + +try: +    with open(file_name, 'w') as f: +        f.write(config.to_string()) +except OSError as e: +    print(f'Failed to save the modified config: {e}') +    exit(1) diff --git a/src/op_mode/ipsec.py b/src/op_mode/ipsec.py index f6417764a..63fa05885 100755 --- a/src/op_mode/ipsec.py +++ b/src/op_mode/ipsec.py @@ -425,7 +425,7 @@ def get_peer_connections(peer, tunnel):      return matches -def reset_peer(peer: str, tunnel:typing.Optional[str]): +def reset_peer(peer: str, tunnel:typing.Optional[str] = None):      conns = get_peer_connections(peer, tunnel)      if not conns: diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server index f59e089ae..cd73f38ec 100755 --- a/src/services/vyos-http-api-server +++ b/src/services/vyos-http-api-server @@ -425,7 +425,7 @@ async def validation_exception_handler(request, exc):      return error(400, str(exc.errors()[0]))  @app.post('/configure') -def configure_op(data: Union[ConfigureModel, ConfigureListModel]): +async def configure_op(data: Union[ConfigureModel, ConfigureListModel]):      session = app.state.vyos_session      env = session.get_session_env()      config = vyos.config.Config(session_env=env) @@ -494,7 +494,7 @@ def configure_op(data: Union[ConfigureModel, ConfigureListModel]):      return success(None)  @app.post("/retrieve") -def retrieve_op(data: RetrieveModel): +async def retrieve_op(data: RetrieveModel):      session = app.state.vyos_session      env = session.get_session_env()      config = vyos.config.Config(session_env=env)  | 
