summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOleksandr Kuchmystyi <o.kuchmystyi@vyos.io>2025-12-31 10:50:21 +0300
committerOleksandr Kuchmystyi <o.kuchmystyi@vyos.io>2026-01-12 16:44:32 +0300
commit61487b13da8dc270907633a3140bd0cc2aa0926a (patch)
tree967c8864116299c4a4d3307bc9666f54b2418761
parent7fea6b6dbbd786bf6a32c496fc70ca9e9b5f8b1f (diff)
downloadvyos-1x-61487b13da8dc270907633a3140bd0cc2aa0926a.tar.gz
vyos-1x-61487b13da8dc270907633a3140bd0cc2aa0926a.zip
openvpn: T7633: Add support for 'data-ciphers-fallback' in site-to-site tunnels
- Introduced new CLI option 'data-ciphers-fallback' for OpenVPN interfaces (used as fallback cipher in site-to-site mode) - Adjust migration 1‑to‑2 to skip migrating 'cipher' for site‑to‑site interfaces
-rw-r--r--data/templates/openvpn/server.conf.j23
-rw-r--r--interface-definitions/include/version/openvpn-version.xml.i2
-rw-r--r--interface-definitions/interfaces_openvpn.xml.in43
-rwxr-xr-xpython/vyos/template.py5
-rw-r--r--smoketest/configs/assert/vpn-openvpn6
-rw-r--r--smoketest/configs/vpn-openvpn12
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_openvpn.py24
-rwxr-xr-xsrc/conf_mode/interfaces_openvpn.py8
-rw-r--r--src/migration-scripts/openvpn/1-to-26
-rw-r--r--src/migration-scripts/openvpn/4-to-538
10 files changed, 146 insertions, 1 deletions
diff --git a/data/templates/openvpn/server.conf.j2 b/data/templates/openvpn/server.conf.j2
index be811f45e..19c8e226c 100644
--- a/data/templates/openvpn/server.conf.j2
+++ b/data/templates/openvpn/server.conf.j2
@@ -217,6 +217,9 @@ cipher {{ encryption.cipher | openvpn_cipher }}
{% if encryption.data_ciphers is vyos_defined %}
data-ciphers {{ encryption.data_ciphers | openvpn_data_ciphers }}
{% endif %}
+{% if encryption.data_ciphers_fallback is vyos_defined %}
+data-ciphers-fallback {{ encryption.data_ciphers_fallback | openvpn_data_ciphers_fallback }}
+{% endif %}
{% endif %}
providers default
diff --git a/interface-definitions/include/version/openvpn-version.xml.i b/interface-definitions/include/version/openvpn-version.xml.i
index 67ef21983..6bc2b2da6 100644
--- a/interface-definitions/include/version/openvpn-version.xml.i
+++ b/interface-definitions/include/version/openvpn-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/openvpn-version.xml.i -->
-<syntaxVersion component='openvpn' version='4'></syntaxVersion>
+<syntaxVersion component='openvpn' version='5'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/interfaces_openvpn.xml.in b/interface-definitions/interfaces_openvpn.xml.in
index 6510ed733..9ce05db9c 100644
--- a/interface-definitions/interfaces_openvpn.xml.in
+++ b/interface-definitions/interfaces_openvpn.xml.in
@@ -131,6 +131,49 @@
<multi/>
</properties>
</leafNode>
+ <leafNode name="data-ciphers-fallback">
+ <properties>
+ <help>Fallback cipher to use for site-to-site tunnels</help>
+ <completionHelp>
+ <list>none 3des aes128 aes128gcm aes192 aes192gcm aes256 aes256gcm</list>
+ </completionHelp>
+ <valueHelp>
+ <format>none</format>
+ <description>Disable encryption</description>
+ </valueHelp>
+ <valueHelp>
+ <format>3des</format>
+ <description>DES algorithm with triple encryption</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes128</format>
+ <description>AES algorithm with 128-bit key CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes128gcm</format>
+ <description>AES algorithm with 128-bit key GCM</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes192</format>
+ <description>AES algorithm with 192-bit key CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes192gcm</format>
+ <description>AES algorithm with 192-bit key GCM</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes256</format>
+ <description>AES algorithm with 256-bit key CBC</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes256gcm</format>
+ <description>AES algorithm with 256-bit key GCM</description>
+ </valueHelp>
+ <constraint>
+ <regex>(none|3des|aes128|aes128gcm|aes192|aes192gcm|aes256|aes256gcm)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
</children>
</node>
#include <include/interface/ipv4-options.xml.i>
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 128471f42..6b3e329a3 100755
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -572,6 +572,11 @@ def get_openvpn_data_ciphers(ciphers):
out.append(cipher)
return ':'.join(out).upper()
+
+@register_filter('openvpn_data_ciphers_fallback')
+def get_openvpn_data_ciphers_fallback(cipher):
+ return get_openvpn_cipher(cipher)
+
@register_filter('snmp_auth_oid')
def snmp_auth_oid(type):
if type not in ['md5', 'sha', 'aes', 'des', 'none']:
diff --git a/smoketest/configs/assert/vpn-openvpn b/smoketest/configs/assert/vpn-openvpn
index 55b1aa879..f7b9857de 100644
--- a/smoketest/configs/assert/vpn-openvpn
+++ b/smoketest/configs/assert/vpn-openvpn
@@ -91,6 +91,12 @@ set interfaces openvpn vtun42 local-port '10042'
set interfaces openvpn vtun42 mode 'site-to-site'
set interfaces openvpn vtun42 remote-address '192.0.2.5'
set interfaces openvpn vtun42 shared-secret-key 'openvpn_vtun40_shared'
+set interfaces openvpn vtun43 local-address 100.64.40.6 subnet-mask '255.255.255.254'
+set interfaces openvpn vtun43 local-port '10043'
+set interfaces openvpn vtun43 mode 'site-to-site'
+set interfaces openvpn vtun43 remote-address '192.0.2.6'
+set interfaces openvpn vtun43 shared-secret-key 'openvpn_vtun40_shared'
+set interfaces openvpn vtun43 encryption data-ciphers-fallback 'aes128'
set pki ca openvpn_vtun10_1 certificate 'MIIFFDCCA/ygAwIBAgIULRXlIT+yqqWW/OCBGnOtDtxmeJowDQYJKoZIhvcNAQELBQAwgasxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMQ4wDAYDVQQHDAVQb3dheTEWMBQGA1UECgwNVnlPUyBOZXR3b3JrczEgMB4GA1UECwwXVnlPUyBOZXR3b3JrcyBUZXN0IENBIDExFDASBgNVBAMMC0Vhc3ktUlNBIENBMScwJQYJKoZIhvcNAQkBFhh0ZXN0LWNhLm5vcmVwbHlAdnlvcy5kZXYwHhcNMjUxMjAxMDc0NDU4WhcNMzUxMTI5MDc0NDU4WjCBqzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExDjAMBgNVBAcMBVBvd2F5MRYwFAYDVQQKDA1WeU9TIE5ldHdvcmtzMSAwHgYDVQQLDBdWeU9TIE5ldHdvcmtzIFRlc3QgQ0EgMTEUMBIGA1UEAwwLRWFzeS1SU0EgQ0ExJzAlBgkqhkiG9w0BCQEWGHRlc3QtY2Eubm9yZXBseUB2eW9zLmRldjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALQHJWBuKQrFvX6pOhpk3FlJaCiwDxPWib0EfMZWeF+CsvHxYH1W/WZfhVW2PcL8Zti0ppoqRsIQpyUDygtwWLjwMCVnQtD9yaA6rUImisQfNsR/ePrFaFK+8fSrGGS4poUnKFueNKzq4V9K7xMFb6+TrecvoclYDEeBMOpCt3dOSCwNlKa2CboUA0mbATXMiIm9lW/1K8bJ7UwRydiEIU9ewi3ZCFXNw7FWj1h3cR1JwxwMPcL9jzm+jlhBTLCy7yiOkdCw7vVFBRa+OaxQ+tTvS+IpPv5f4/9Hr19IqmsR5HBOHJrYvgD3LDGFZZ1uuKeG4CM8N8G7K9DxqXvEl7ECAwEAAaOCASwwggEoMB0GA1UdDgQWBBTh+x9ektMHGHxAY/+cZMng8fuGpTCB6wYDVR0jBIHjMIHggBTh+x9ektMHGHxAY/+cZMng8fuGpaGBsaSBrjCBqzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExDjAMBgNVBAcMBVBvd2F5MRYwFAYDVQQKDA1WeU9TIE5ldHdvcmtzMSAwHgYDVQQLDBdWeU9TIE5ldHdvcmtzIFRlc3QgQ0EgMTEUMBIGA1UEAwwLRWFzeS1SU0EgQ0ExJzAlBgkqhkiG9w0BCQEWGHRlc3QtY2Eubm9yZXBseUB2eW9zLmRldoIULRXlIT+yqqWW/OCBGnOtDtxmeJowDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBACgqIP4xZo6ioKnlNLhix4WRp0tRkveAKJ144ylCzzzP5+pdPQKS5hlydDcNxrJB6EYlkmaWBsvYt7NO9oII4wwD7uvD7Tnad31ANc9lkD8qEksoVy5Sc9HpYQvo+a8ZH/Co8ciTse/eUAIWzp8GHTH40HeqGtJGvLF4pHXxHYBO8vuZAL0Bs8hl7ihyOrRE5fkwjmWH3RcsavTF5jhAOsRsInUiqLUafFChvte3z739irjCaIK0lt0ZWfKvWVeV+JeNgwl0mqNq78FZRQa7rC72x3UJJwV1xRcPp4Y+WW7Xb8gTZqkuMzBW3xgYuKlOmHOz1Ta2ktEteDdS5PF/c+g='
set pki ca openvpn_vtun10_1 crl 'MIIDDzCCAfcCAQEwDQYJKoZIhvcNAQELBQAwgasxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMQ4wDAYDVQQHDAVQb3dheTEWMBQGA1UECgwNVnlPUyBOZXR3b3JrczEgMB4GA1UECwwXVnlPUyBOZXR3b3JrcyBUZXN0IENBIDExFDASBgNVBAMMC0Vhc3ktUlNBIENBMScwJQYJKoZIhvcNAQkBFhh0ZXN0LWNhLm5vcmVwbHlAdnlvcy5kZXYXDTI1MTIwMTA5MjYwOFoXDTI2MDUzMDA5MjYwOFowIzAhAhAQ70nBplg7b4g0yD0jTde6Fw0yNTEyMDEwOTI2MDBaoIHxMIHuMIHrBgNVHSMEgeMwgeCAFOH7H16S0wcYfEBj/5xkyeDx+4aloYGxpIGuMIGrMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEOMAwGA1UEBwwFUG93YXkxFjAUBgNVBAoMDVZ5T1MgTmV0d29ya3MxIDAeBgNVBAsMF1Z5T1MgTmV0d29ya3MgVGVzdCBDQSAxMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTEnMCUGCSqGSIb3DQEJARYYdGVzdC1jYS5ub3JlcGx5QHZ5b3MuZGV2ghQtFeUhP7KqpZb84IEac60O3GZ4mjANBgkqhkiG9w0BAQsFAAOCAQEAFVgGK+EIWsTO1d3PGeh+Sxp9etu60opYYcvtlEqZsyLOp8oJYslo62BNcrxzLIoRTViEVqyFjxyl2+mTk9q/REZqcSSesBpdkzErdVLixCEeIfSgmBd3yoqih2gGR+fEibG6ETbYAc4fzZN761F5Z7Wurm65s5Xgl4X6Vl989kskiw8C0ESeSUKciINPmwWbwTRvJRStJve5NtkqpM6Rdq0SoTA0XWQqRcc7Aicv3/SI5aPpEGTLFKuRoCgGGhnTQywaNACU/BykbjGiiFcwNWpfHrYj3lCFm2Gyw9Sw70PT1RGBDq9yBSa0Px2ri1WjhnYF58PjrW3m1laoI+LkIg=='
set pki ca openvpn_vtun20_1 certificate 'MIIFFDCCA/ygAwIBAgIUCKOAES495hrgXHMl0IQb1nkcWsEwDQYJKoZIhvcNAQELBQAwgasxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMQ4wDAYDVQQHDAVQb3dheTEWMBQGA1UECgwNVnlPUyBOZXR3b3JrczEgMB4GA1UECwwXVnlPUyBOZXR3b3JrcyBUZXN0IENBIDIxFDASBgNVBAMMC0Vhc3ktUlNBIENBMScwJQYJKoZIhvcNAQkBFhh0ZXN0LWNhLm5vcmVwbHlAdnlvcy5kZXYwHhcNMjUxMjAxMDc1MzE5WhcNMzUxMTI5MDc1MzE5WjCBqzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExDjAMBgNVBAcMBVBvd2F5MRYwFAYDVQQKDA1WeU9TIE5ldHdvcmtzMSAwHgYDVQQLDBdWeU9TIE5ldHdvcmtzIFRlc3QgQ0EgMjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0ExJzAlBgkqhkiG9w0BCQEWGHRlc3QtY2Eubm9yZXBseUB2eW9zLmRldjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMxcxr7KKqKuUOWpyE/GZ2BYvMxXEdDavQS3Ok2n5hFAnCAqtjCx63z26P18xJdqkpWpsyTxN5aEGgXyFUIL6kiceL4D9vIO52swI8QaRi/4V7X86wJQqUaQYg7FMboeyBsLXFMl+TX1vkX/bgFiGSfqS0oTPZt68GO6jHxbE1tqqhS2qQSVt/4WJMyZ3BFjQpz0szwcVh2tHKqSSuhpS0MY8MKpeITo6LhPPphfpdBn6fizydj1IEdXD22YJK+YUYv2AFY5V6GzYYQpO2oGe9CGW68a/Q+OtocZYvjpCIm21UFnfkxz4vdW7vNgBSXvfKOEZ+ZhKzaNL6Ftj0afJdsCAwEAAaOCASwwggEoMB0GA1UdDgQWBBTaP0CGONZGw8I/Bze7xjO1p3dKszCB6wYDVR0jBIHjMIHggBTaP0CGONZGw8I/Bze7xjO1p3dKs6GBsaSBrjCBqzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExDjAMBgNVBAcMBVBvd2F5MRYwFAYDVQQKDA1WeU9TIE5ldHdvcmtzMSAwHgYDVQQLDBdWeU9TIE5ldHdvcmtzIFRlc3QgQ0EgMjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0ExJzAlBgkqhkiG9w0BCQEWGHRlc3QtY2Eubm9yZXBseUB2eW9zLmRldoIUCKOAES495hrgXHMl0IQb1nkcWsEwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBADjhWXjaR5EZoqHBD+H2F+YFg4oxf5vTr37JrsDINZG2cDprGQUl/8MrjziuVobtFzFLMlYwI+s8ASV1eYa+8yGrZFspsaJ3FkvWLx1CRCbP+jH+rJu+PzkcNRf5RinNxRVt/TnZlWKGAACsoGyzQJ3bg0NQKb47W0vD6PZ4nKK3SWvLdLBPW0bYpe62S3fE188fq4gr4Ig4J0Egi2aZhX/MHpT9bYGvQih+FgY33aQiGQphX1FX2n4kC7fwcJlzlp+mMhmnjWwuWSxRMFGAp6aJlPBvWbE3GyRx0b7skH7xmbRpV3DYbzxAxpzXw5o2xUx/tyQKW76Xp8nKZtK7jQM='
diff --git a/smoketest/configs/vpn-openvpn b/smoketest/configs/vpn-openvpn
index e50a49aba..95a7e76f9 100644
--- a/smoketest/configs/vpn-openvpn
+++ b/smoketest/configs/vpn-openvpn
@@ -214,6 +214,18 @@ interfaces {
remote-address 192.0.2.5
shared-secret-key-file /config/auth/openvpn/shared-secret-2.key
}
+ openvpn vtun43 {
+ encryption {
+ cipher aes128
+ }
+ local-address 100.64.40.6 {
+ subnet-mask 255.255.255.254
+ }
+ local-port 10043
+ mode site-to-site
+ remote-address 192.0.2.6
+ shared-secret-key-file /config/auth/openvpn/shared-secret-2.key
+ }
}
protocols {
ospf {
diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py
index e646ec6fd..9286a8c0e 100755
--- a/smoketest/scripts/cli/test_interfaces_openvpn.py
+++ b/smoketest/scripts/cli/test_interfaces_openvpn.py
@@ -816,6 +816,30 @@ class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
+ def test_site2site_data_ciphers_fallback(self):
+ vtun_if = 'vtun2010'
+ path = ['interfaces', 'openvpn', vtun_if]
+
+ # Configure a minimal site-to-site tunnel with data-ciphers-fallback
+ self.cli_set(path + ['mode', 'site-to-site'])
+ self.cli_set(path + ['encryption', 'data-ciphers-fallback', 'aes192'])
+ self.cli_set(path + ['local-address', '10.0.1.1'])
+ self.cli_set(path + ['remote-address', '10.0.1.2'])
+ self.cli_set(path + ['shared-secret-key', 'ovpn_test'])
+
+ self.cli_commit()
+
+ config_file = f'/run/openvpn/{vtun_if}.conf'
+ config = read_file(config_file)
+
+ # Validate correct OpenVPN configuration rendering
+ self.assertIn(f'dev {vtun_if}', config)
+ self.assertIn('data-ciphers-fallback AES-192-CBC', config)
+
+ # Ensure no other directives are rendered
+ self.assertNotIn('cipher ', config)
+ self.assertNotIn('data-ciphers ', config)
+
def test_openvpn_server_server_bridge(self):
# Create OpenVPN server interface using bridge.
# Validate configuration afterwards.
diff --git a/src/conf_mode/interfaces_openvpn.py b/src/conf_mode/interfaces_openvpn.py
index 5cc09da89..bb9d06389 100755
--- a/src/conf_mode/interfaces_openvpn.py
+++ b/src/conf_mode/interfaces_openvpn.py
@@ -168,6 +168,12 @@ def is_ec_private_key(pki, cert_name):
key = load_private_key(pki_cert['private']['key'])
return isinstance(key, ec.EllipticCurvePrivateKey)
+
+def verify_data_ciphers_fallback(openvpn):
+ if openvpn['mode'] != 'site-to-site':
+ if dict_search('encryption.data_ciphers_fallback', openvpn):
+ raise ConfigError('Cipher fallback is valid only in site-to-site mode')
+
def verify_pki(openvpn):
pki = openvpn['pki']
interface = openvpn['ifname']
@@ -615,6 +621,8 @@ def verify(openvpn):
verify_bond_bridge_member(openvpn)
verify_mirror_redirect(openvpn)
+ verify_data_ciphers_fallback(openvpn)
+
return None
def generate_pki_files(openvpn):
diff --git a/src/migration-scripts/openvpn/1-to-2 b/src/migration-scripts/openvpn/1-to-2
index 83d6219d6..75360446c 100644
--- a/src/migration-scripts/openvpn/1-to-2
+++ b/src/migration-scripts/openvpn/1-to-2
@@ -25,11 +25,17 @@ def migrate(config: ConfigTree) -> None:
# Remove 'encryption cipher' and add this value to 'encryption ncp-ciphers'
# for server and client mode.
# Site-to-site mode still can use --cipher option
+ mode_path = ['interfaces', 'openvpn', i, 'mode']
cipher_path = ['interfaces', 'openvpn', i, 'encryption', 'cipher']
ncp_cipher_path = ['interfaces', 'openvpn', i, 'encryption', 'ncp-ciphers']
+
if config.exists(cipher_path):
if config.exists(['interfaces', 'openvpn', i, 'shared-secret-key']):
continue
+ # Only migrate if mode is not 'site-to-site'
+ if config.value_exists(mode_path, 'site-to-site'):
+ continue
+
cipher = config.return_value(cipher_path)
config.delete(cipher_path)
if cipher == 'none':
diff --git a/src/migration-scripts/openvpn/4-to-5 b/src/migration-scripts/openvpn/4-to-5
new file mode 100644
index 000000000..5c7ee833c
--- /dev/null
+++ b/src/migration-scripts/openvpn/4-to-5
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see <http://www.gnu.org/licenses/>.
+#
+# T7633: This migration converts legacy 'encryption cipher' directives into
+# 'encryption data-ciphers-fallback' for site-to-site mode tunnels.
+#
+# In modern OpenVPN (v2.6+), 'cipher' and 'data-ciphers' are not valid
+# in site-to-site mode.
+# The appropriate directive is now '--data-ciphers-fallback alg'.
+
+from vyos.configtree import ConfigTree
+
+def migrate(config: ConfigTree) -> None:
+ ovpn_intfs = config.list_nodes(['interfaces', 'openvpn'], path_must_exist=False)
+ for i in ovpn_intfs:
+ base_path = ['interfaces', 'openvpn', i]
+ mode_path = base_path + ['mode']
+ cipher_path = base_path + ['encryption', 'cipher']
+
+ # Only migrate if mode is explicitly 'site-to-site'
+ if config.value_exists(mode_path, 'site-to-site'):
+
+ # Rename 'encryption cipher' with 'encryption data-ciphers-fallback'
+ if config.exists(cipher_path):
+ config.rename(cipher_path, 'data-ciphers-fallback')