Add support for additional auth, encryption, PFS choices

Encryption algorithms: add AES CCM mode and AES GCM mode variants
for 128/192/256 bit keys and 8/12/16 octet ICVs.
In the API that will be 9 new choices for AES CCM and 9 for AES GCM,
e.g. aes-256-ccm-16 (aes-{keysize}-ccm-{icv-size}).
Add encrpytion algorithms for AES CTR mode: aes-128-ctr, aes-192-ctr,
aes-256-ctr.
Auth algorithms: add aes-xcbc and aes-cmac.
PFS: add Diffie Hellman groups 15 to 31.

Closes-Bug: #1938284
Depends-On: https://review.opendev.org/c/openstack/neutron-lib/+/903971
Change-Id: I07f49d8e91f0f16ee4c97e636ab3b62a5692d70c
This commit is contained in:
Bodo Petermann 2023-10-18 13:58:44 +02:00
parent 256464aea6
commit a5fc227751
10 changed files with 213 additions and 23 deletions

View File

@ -0,0 +1,80 @@
# Copyright 2023 SysEleven GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from neutron.db import migration
import sqlalchemy as sa
"""add more ciphers
Revision ID: b18aab30fddc
Revises: 22e0145ac80b
Create Date: 2023-10-11 15:40:27.845720
"""
# revision identifiers, used by Alembic.
revision = 'b18aab30fddc'
down_revision = '22e0145ac80b'
AUTH_ALGORITHM_ENUM_VALUES = [
'sha1', 'sha256', 'sha384', 'sha512',
'aes-xcbc', 'aes-cmac',
]
ENCRYPTION_ALGORITHM_ENUM_VALUES = [
'3des',
'aes-128', 'aes-192', 'aes-256',
'aes-128-ctr', 'aes-192-ctr', 'aes-256-ctr',
'aes-128-ccm-8', 'aes-192-ccm-8', 'aes-256-ccm-8',
'aes-128-ccm-12', 'aes-192-ccm-12', 'aes-256-ccm-12',
'aes-128-ccm-16', 'aes-192-ccm-16', 'aes-256-ccm-16',
'aes-128-gcm-8', 'aes-192-gcm-8', 'aes-256-gcm-8',
'aes-128-gcm-12', 'aes-192-gcm-12', 'aes-256-gcm-12',
'aes-128-gcm-16', 'aes-192-gcm-16', 'aes-256-gcm-16',
]
PFS_ENUM_VALUES = [
'group2', 'group5', 'group14', 'group15',
'group16', 'group17', 'group18', 'group19', 'group20', 'group21',
'group22', 'group23', 'group24', 'group25', 'group26', 'group27',
'group28', 'group29', 'group30', 'group31',
]
def upgrade():
migration.alter_enum('ikepolicies', 'pfs',
sa.Enum(*PFS_ENUM_VALUES, name="vpn_pfs"),
nullable=False, do_drop=False)
migration.alter_enum('ikepolicies', 'auth_algorithm',
sa.Enum(*AUTH_ALGORITHM_ENUM_VALUES,
name="vpn_auth_algorithms"),
nullable=False, do_drop=False)
migration.alter_enum('ikepolicies', 'encryption_algorithm',
sa.Enum(*ENCRYPTION_ALGORITHM_ENUM_VALUES,
name="vpn_encrypt_algorithms"),
nullable=False, do_drop=False)
migration.alter_enum('ipsecpolicies', 'pfs',
sa.Enum(*PFS_ENUM_VALUES, name="vpn_pfs"),
nullable=False, do_rename=False, do_create=False)
migration.alter_enum('ipsecpolicies', 'auth_algorithm',
sa.Enum(*AUTH_ALGORITHM_ENUM_VALUES,
name="vpn_auth_algorithms"),
nullable=False, do_rename=False, do_create=False)
migration.alter_enum('ipsecpolicies', 'encryption_algorithm',
sa.Enum(*ENCRYPTION_ALGORITHM_ENUM_VALUES,
name="vpn_encrypt_algorithms"),
nullable=False, do_rename=False, do_create=False)

View File

@ -1 +1 @@
22e0145ac80b
b18aab30fddc

View File

@ -24,6 +24,31 @@ from neutron.db import models_v2
from neutron_vpnaas.services.vpn.common import constants
AUTH_ALGORITHM_ENUM_VALUES = [
'sha1', 'sha256', 'sha384', 'sha512',
'aes-xcbc', 'aes-cmac',
]
ENCRYPTION_ALGORITHM_ENUM_VALUES = [
'3des',
'aes-128', 'aes-192', 'aes-256',
'aes-128-ctr', 'aes-192-ctr', 'aes-256-ctr',
'aes-128-ccm-8', 'aes-192-ccm-8', 'aes-256-ccm-8',
'aes-128-ccm-12', 'aes-192-ccm-12', 'aes-256-ccm-12',
'aes-128-ccm-16', 'aes-192-ccm-16', 'aes-256-ccm-16',
'aes-128-gcm-8', 'aes-192-gcm-8', 'aes-256-gcm-8',
'aes-128-gcm-12', 'aes-192-gcm-12', 'aes-256-gcm-12',
'aes-128-gcm-16', 'aes-192-gcm-16', 'aes-256-gcm-16',
]
PFS_ENUM_VALUES = [
'group2', 'group5', 'group14', 'group15',
'group16', 'group17', 'group18', 'group19', 'group20', 'group21',
'group22', 'group23', 'group24', 'group25', 'group26', 'group27',
'group28', 'group29', 'group30', 'group31',
]
class IPsecPeerCidr(model_base.BASEV2):
"""Internal representation of a IPsec Peer Cidrs."""
@ -43,12 +68,10 @@ class IPsecPolicy(model_base.BASEV2, model_base.HasId, model_base.HasProject):
transform_protocol = sa.Column(sa.Enum("esp", "ah", "ah-esp",
name="ipsec_transform_protocols"),
nullable=False)
auth_algorithm = sa.Column(sa.Enum("sha1", "sha256",
"sha384", "sha512",
auth_algorithm = sa.Column(sa.Enum(*AUTH_ALGORITHM_ENUM_VALUES,
name="vpn_auth_algorithms"),
nullable=False)
encryption_algorithm = sa.Column(sa.Enum("3des", "aes-128",
"aes-256", "aes-192",
encryption_algorithm = sa.Column(sa.Enum(*ENCRYPTION_ALGORITHM_ENUM_VALUES,
name="vpn_encrypt_algorithms"),
nullable=False)
encapsulation_mode = sa.Column(sa.Enum("tunnel", "transport",
@ -58,8 +81,7 @@ class IPsecPolicy(model_base.BASEV2, model_base.HasId, model_base.HasProject):
name="vpn_lifetime_units"),
nullable=False)
lifetime_value = sa.Column(sa.Integer, nullable=False)
pfs = sa.Column(sa.Enum("group2", "group5", "group14",
name="vpn_pfs"), nullable=False)
pfs = sa.Column(sa.Enum(*PFS_ENUM_VALUES, name="vpn_pfs"), nullable=False)
class IKEPolicy(model_base.BASEV2, model_base.HasId, model_base.HasProject):
@ -67,12 +89,10 @@ class IKEPolicy(model_base.BASEV2, model_base.HasId, model_base.HasProject):
__tablename__ = 'ikepolicies'
name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE))
description = sa.Column(sa.String(db_const.DESCRIPTION_FIELD_SIZE))
auth_algorithm = sa.Column(sa.Enum("sha1", "sha256",
"sha384", "sha512",
auth_algorithm = sa.Column(sa.Enum(*AUTH_ALGORITHM_ENUM_VALUES,
name="vpn_auth_algorithms"),
nullable=False)
encryption_algorithm = sa.Column(sa.Enum("3des", "aes-128",
"aes-256", "aes-192",
encryption_algorithm = sa.Column(sa.Enum(*ENCRYPTION_ALGORITHM_ENUM_VALUES,
name="vpn_encrypt_algorithms"),
nullable=False)
phase1_negotiation_mode = sa.Column(sa.Enum("main", 'aggressive',
@ -84,8 +104,7 @@ class IKEPolicy(model_base.BASEV2, model_base.HasId, model_base.HasProject):
lifetime_value = sa.Column(sa.Integer, nullable=False)
ike_version = sa.Column(sa.Enum("v1", "v2", name="ike_versions"),
nullable=False)
pfs = sa.Column(sa.Enum("group2", "group5", "group14",
name="vpn_pfs"), nullable=False)
pfs = sa.Column(sa.Enum(*PFS_ENUM_VALUES, name="vpn_pfs"), nullable=False)
class IPsecSiteConnection(model_base.BASEV2, model_base.HasId,

View File

@ -161,8 +161,12 @@ class BaseSwanProcess(object, metaclass=abc.ABCMeta):
DIALECT_MAP = {
"3des": "3des",
"aes-128": "aes128",
"aes-256": "aes256",
"aes-192": "aes192",
"aes-256": "aes256",
"aes-ctr-128": "aesctr128",
"aes-ctr-192": "aesctr192",
"aes-ctr-256": "aesctr256",
"aes-xcbc": "aes_xcbc",
"sha256": "sha2_256",
"sha384": "sha2_384",
"sha512": "sha2_512",
@ -170,6 +174,16 @@ class BaseSwanProcess(object, metaclass=abc.ABCMeta):
"group5": "modp1536",
"group14": "modp2048",
"group15": "modp3072",
"group16": "modp4096",
"group17": "modp6144",
"group18": "modp8192",
"group19": "ecp256",
"group20": "ecp384",
"group21": "ecp521",
"group22": "dh22",
"group23": "dh23",
"group24": "dh24",
"group31": "curve25519",
"bi-directional": "start",
"response-only": "add",
"v2": "insist",

View File

@ -76,9 +76,53 @@ class StrongSwanProcess(ipsec.BaseSwanProcess):
STATUS_NOT_RUNNING_RE = 'Command:.*ipsec.*status.*Exit code: [1|3] '
def __init__(self, conf, process_id, vpnservice, namespace):
self.DIALECT_MAP['v1'] = 'ikev1'
self.DIALECT_MAP['v2'] = 'ikev2'
self.DIALECT_MAP['sha256'] = 'sha256'
dialect_map_update = {
'v1': 'ikev1',
'v2': 'ikev2',
# ENCR_AES_CTR
'aes-128-ctr': 'aes128ctr',
'aes-192-ctr': 'aes192ctr',
'aes-256-ctr': 'aes256ctr',
# ENCR_AES_CCM_8
'aes-128-ccm-8': 'aes128ccm8',
'aes-192-ccm-8': 'aes192ccm8',
'aes-256-ccm-8': 'aes256ccm8',
# ENCR_AES_CCM_12
'aes-128-ccm-12': 'aes128ccm12',
'aes-192-ccm-12': 'aes192ccm12',
'aes-256-ccm-12': 'aes256ccm12',
# ENCR_AES_CCM_16
'aes-128-ccm-16': 'aes128ccm16',
'aes-192-ccm-16': 'aes192ccm16',
'aes-256-ccm-16': 'aes256ccm16',
# ENCR_AES_GCM_8
'aes-128-gcm-8': 'aes128gcm8',
'aes-192-gcm-8': 'aes192gcm8',
'aes-256-gcm-8': 'aes256gcm8',
# ENCR_AES_GCM_12
'aes-128-gcm-12': 'aes128gcm12',
'aes-192-gcm-12': 'aes192gcm12',
'aes-256-gcm-12': 'aes256gcm12',
# ENCR_AES_GCM_16
'aes-128-gcm-16': 'aes128gcm16',
'aes-192-gcm-16': 'aes192gcm16',
'aes-256-gcm-16': 'aes256gcm16',
# AUTH
'sha256': 'sha256',
'aes-xcbc': 'aesxcbc',
'aes-cmac': 'aescmac',
# PFS
'group22': 'modp1024s160',
'group23': 'modp2048s224',
'group24': 'modp2048s256',
'group25': 'ecp192',
'group26': 'ecp224',
'group27': 'ecp224bp',
'group28': 'ecp256bp',
'group29': 'ecp384bp',
'group30': 'ecp512bp',
}
self.DIALECT_MAP.update(dialect_map_update)
self._strongswan_piddir = self._get_strongswan_piddir()
self._rootwrap_cfg = self._get_rootwrap_config()
LOG.debug("strongswan piddir is '%s'", (self._strongswan_piddir))

View File

@ -61,7 +61,7 @@ conn {{ipsec_site_connection.id}}
######################
#ike version
ikev2={{ipsec_site_connection.ikepolicy.ike_version}}
# [encryption_algorithm]-[auth_algorithm]-[pfs]
# [encryption_algorithm]-[auth_algorithm];[pfs]
ike={{ipsec_site_connection.ikepolicy.encryption_algorithm}}-{{ipsec_site_connection.ikepolicy.auth_algorithm}};{{ipsec_site_connection.ikepolicy.pfs}}
{% if ipsec_site_connection.ikepolicy.phase1_negotiation_mode == "aggressive" -%}
aggressive=yes
@ -76,10 +76,13 @@ conn {{ipsec_site_connection.id}}
phase2={{ipsec_site_connection.ipsecpolicy.transform_protocol}}
{% if ipsec_site_connection.ipsecpolicy.transform_protocol == "ah" -%}
# AH protocol does not support encryption
# [auth_algorithm]-[pfs]
# [auth_algorithm];[pfs]
phase2alg={{ipsec_site_connection.ipsecpolicy.auth_algorithm}};{{ipsec_site_connection.ipsecpolicy.pfs}}
{% elif 'cm' in ipsec_site_connection.ipsecpolicy.encryption_algorithm -%}
# [encryption_algorithm];[pfs]
phase2alg={{ipsec_site_connection.ipsecpolicy.encryption_algorithm}};{{ipsec_site_connection.ipsecpolicy.pfs}}
{% else -%}
# [encryption_algorithm]-[auth_algorithm]-[pfs]
# [encryption_algorithm]-[auth_algorithm];[pfs]
phase2alg={{ipsec_site_connection.ipsecpolicy.encryption_algorithm}}-{{ipsec_site_connection.ipsecpolicy.auth_algorithm}};{{ipsec_site_connection.ipsecpolicy.pfs}}
{% endif -%}
# [encapsulation_mode]

View File

@ -28,6 +28,8 @@ conn {{ipsec_site_connection.id}}
{%- endif %}
{%- if ipsec_site_connection.ipsecpolicy.transform_protocol == "ah" %}
ah={{ipsec_site_connection.ipsecpolicy.auth_algorithm}}-{{ipsec_site_connection.ipsecpolicy.pfs}}
{%- elif 'cm' in ipsec_site_connection.ipsecpolicy.encryption_algorithm %}
esp={{ipsec_site_connection.ipsecpolicy.encryption_algorithm}}-{{ipsec_site_connection.ipsecpolicy.pfs}}
{%- else %}
esp={{ipsec_site_connection.ipsecpolicy.encryption_algorithm}}-{{ipsec_site_connection.ipsecpolicy.auth_algorithm}}-{{ipsec_site_connection.ipsecpolicy.pfs}}
{%- endif %}

View File

@ -515,6 +515,16 @@ class TestVpnaas(VPNPluginDbTestCase):
with self.ikepolicy(name=name, description=description) as ikepolicy:
self._check_policy(ikepolicy['ikepolicy'], keys, lifetime)
def test_create_ikepolicy_with_every_pfs(self):
"""Test case to create ikepolicies with different pfs."""
pfs_list = ['group2', 'group5', 'group14', 'group15', 'group16',
'group17', 'group18', 'group19', 'group20', 'group21',
'group22', 'group23', 'group24', 'group25', 'group26',
'group27', 'group28', 'group29', 'group30', 'group31']
name = "ikepolicy1"
for group in pfs_list:
self.ikepolicy(name=name, pfs=group, expected_res_status=201)
def test_create_ikepolicy_with_aggressive_mode(self):
"""Test case to create an ikepolicy with aggressive mode."""
name = "ikepolicy1"
@ -748,6 +758,16 @@ class TestVpnaas(VPNPluginDbTestCase):
description=description) as ipsecpolicy:
self._check_policy(ipsecpolicy['ipsecpolicy'], keys, lifetime)
def test_create_ipsecpolicies_with_every_pfs(self):
"""Test case to create ipsecpolicies with different pfs."""
pfs_list = ['group2', 'group5', 'group14', 'group15', 'group16',
'group17', 'group18', 'group19', 'group20', 'group21',
'group22', 'group23', 'group24', 'group25', 'group26',
'group27', 'group28', 'group29', 'group30', 'group31']
name = "ipsecpolicy1"
for group in pfs_list:
self.ipsecpolicy(name=name, pfs=group, expected_res_status=201)
def test_delete_ipsecpolicy(self):
"""Test case to delete an ipsecpolicy."""
with self.ipsecpolicy(do_delete=False) as ipsecpolicy:

View File

@ -107,12 +107,12 @@ FAKE_VPN_SERVICE = {
}
AUTH_ESP = '''esp
# [encryption_algorithm]-[auth_algorithm]-[pfs]
# [encryption_algorithm]-[auth_algorithm];[pfs]
phase2alg=aes128-sha1;modp1536'''
AUTH_AH = '''ah
# AH protocol does not support encryption
# [auth_algorithm]-[pfs]
# [auth_algorithm];[pfs]
phase2alg=sha1;modp1536'''
OPENSWAN_CONNECTION_DETAILS = '''# rightsubnet=networkA/netmaskA, networkB/netmaskB (IKEv2 only)
@ -131,7 +131,7 @@ OPENSWAN_CONNECTION_DETAILS = '''# rightsubnet=networkA/netmaskA, networkB/netma
######################
#ike version
ikev2=never
# [encryption_algorithm]-[auth_algorithm]-[pfs]
# [encryption_algorithm]-[auth_algorithm];[pfs]
ike=aes128-sha1;modp1536
# [lifetime_value]
ikelifetime=%(ike_lifetime)ss

View File

@ -0,0 +1,8 @@
---
prelude: >
Support for AES CCM and GCM modes, AES-XCBC, AES-CMAC, and more DH groups
features:
- |
Added support for more encryption algorithms (AES CCM and AES GCM modes),
authentication algorithms (AES-XCBC, AES-CMAC) and PFS choices
(Diffie Hellman groups 15 to 31).