Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

T6617: T6618: vpn ipsec remote-access: fix profile generators #3903

Merged
merged 1 commit into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion data/templates/ipsec/ios_profile.j2
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@
<!-- The server is authenticated using a certificate -->
<key>AuthenticationMethod</key>
<string>Certificate</string>
{% if authentication.client_mode.startswith("eap") %}
<!-- The client uses EAP to authenticate -->
<key>ExtendedAuthEnabled</key>
<integer>1</integer>
{% endif %}
<!-- The next two dictionaries are optional (as are the keys in them), but it is recommended to specify them as the default is to use 3DES.
IMPORTANT: Because only one proposal is sent (even if nothing is configured here) it must match the server configuration -->
<key>IKESecurityAssociationParameters</key>
Expand All @@ -78,9 +80,14 @@
<string>{{ esp_encryption.encryption }}</string>
<key>IntegrityAlgorithm</key>
<string>{{ esp_encryption.hash }}</string>
{% if esp_encryption.pfs is vyos_defined %}
<key>DiffieHellmanGroup</key>
<integer>{{ ike_encryption.dh_group }}</integer>
<integer>{{ esp_encryption.pfs }}</integer>
{% endif %}
</dict>
<!-- Controls whether the client offers Perfect Forward Secrecy (PFS). This should be set to match the server. -->
<key>EnablePFS</key>
<integer>{{ '1' if esp_encryption.pfs is vyos_defined else '0' }}</integer>
</dict>
</dict>
{% if ca_certificates is vyos_defined %}
Expand Down
2 changes: 1 addition & 1 deletion data/templates/ipsec/windows_profile.j2
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Remove-VpnConnection -Name "{{ vpn_name }}" -Force -PassThru

Add-VpnConnection -Name "{{ vpn_name }}" -ServerAddress "{{ remote }}" -TunnelType "Ikev2"
Set-VpnConnectionIPsecConfiguration -ConnectionName "{{ vpn_name }}" -AuthenticationTransformConstants {{ ike_encryption.encryption }} -CipherTransformConstants {{ ike_encryption.encryption }} -EncryptionMethod {{ esp_encryption.encryption }} -IntegrityCheckMethod {{ esp_encryption.hash }} -PfsGroup None -DHGroup "Group{{ ike_encryption.dh_group }}" -PassThru -Force
Set-VpnConnectionIPsecConfiguration -ConnectionName "{{ vpn_name }}" -AuthenticationTransformConstants {{ ike_encryption.encryption }} -CipherTransformConstants {{ ike_encryption.encryption }} -EncryptionMethod {{ esp_encryption.encryption }} -IntegrityCheckMethod {{ esp_encryption.hash }} -PfsGroup {{ esp_encryption.pfs }} -DHGroup {{ ike_encryption.dh_group }} -PassThru -Force
85 changes: 74 additions & 11 deletions src/op_mode/ikev2_profile_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,39 @@
}

# IOS 14.2 and later do no support dh-group 1,2 and 5. Supported DH groups would
# be: 14, 15, 16, 17, 18, 19, 20, 21, 31
ios_supported_dh_groups = ['14', '15', '16', '17', '18', '19', '20', '21', '31']
# Windows 10 only allows a limited set of DH groups
windows_supported_dh_groups = ['1', '2', '14', '24']
# be: 14, 15, 16, 17, 18, 19, 20, 21, 31, 32
vyos2apple_dh_group = {
'14' : '14',
'15' : '15',
'16' : '16',
'17' : '17',
'18' : '18',
'19' : '19',
'20' : '20',
'21' : '21',
'31' : '31',
'32' : '32'
}

# Newer versions of Windows support groups 19 and 20, albeit under a different naming convention
vyos2windows_dh_group = {
'1' : 'Group1',
'2' : 'Group2',
'14' : 'Group14',
'19' : 'ECP256',
'20' : 'ECP384',
'24' : 'Group24'
}

# For PFS, Windows also has its own inconsistent naming scheme for each group
vyos2windows_pfs_group = {
'1' : 'PFS1',
'2' : 'PFS2',
'14' : 'PFS2048',
'19' : 'ECP256',
'20' : 'ECP384',
'24' : 'PFS24'
}

parser = argparse.ArgumentParser()
parser.add_argument('--os', const='all', nargs='?', choices=['ios', 'windows'], help='Operating system used for config generation', required=True)
Expand Down Expand Up @@ -181,7 +210,7 @@
# https://stackoverflow.com/a/9427216
data['ca_certificates'] = [dict(t) for t in {tuple(d.items()) for d in data['ca_certificates']}]

esp_proposals = conf.get_config_dict(ipsec_base + ['esp-group', data['esp_group'], 'proposal'],
esp_group = conf.get_config_dict(ipsec_base + ['esp-group', data['esp_group']],
key_mangling=('-', '_'), get_first_key=True)
ike_proposal = conf.get_config_dict(ipsec_base + ['ike-group', data['ike_group'], 'proposal'],
key_mangling=('-', '_'), get_first_key=True)
Expand All @@ -192,7 +221,29 @@

vyos2client_cipher = vyos2apple_cipher if args.os == 'ios' else vyos2windows_cipher;
vyos2client_integrity = vyos2apple_integrity if args.os == 'ios' else vyos2windows_integrity;
supported_dh_groups = ios_supported_dh_groups if args.os == 'ios' else windows_supported_dh_groups;
vyos2client_dh_group = vyos2apple_dh_group if args.os == 'ios' else vyos2windows_dh_group

def transform_pfs(pfs, ike_dh_group):
pfs_enabled = (pfs != 'disable')
if pfs == 'enable':
pfs_dh_group = ike_dh_group
elif pfs.startswith('dh-group'):
pfs_dh_group = pfs.removeprefix('dh-group')

if args.os == 'ios':
if pfs_enabled:
if pfs_dh_group not in set(vyos2apple_dh_group):
exit(f'The PFS group configured for "{args.connection}" is not supported by the client!')
return pfs_dh_group
else:
return None
else:
if pfs_enabled:
if pfs_dh_group not in set(vyos2windows_pfs_group):
exit(f'The PFS group configured for "{args.connection}" is not supported by the client!')
return vyos2windows_pfs_group[ pfs_dh_group ]
else:
return 'None'

# Create a dictionary containing client conform IKE settings
ike = {}
Expand All @@ -201,24 +252,28 @@
if {'dh_group', 'encryption', 'hash'} <= set(proposal):
if (proposal['encryption'] in set(vyos2client_cipher) and
proposal['hash'] in set(vyos2client_integrity) and
proposal['dh_group'] in set(supported_dh_groups)):
proposal['dh_group'] in set(vyos2client_dh_group)):

# We 're-code' from the VyOS IPsec proposals to the Apple naming scheme
proposal['encryption'] = vyos2client_cipher[ proposal['encryption'] ]
proposal['hash'] = vyos2client_integrity[ proposal['hash'] ]
# DH group will need to be transformed later after we calculate PFS group

ike.update( { str(count) : proposal } )
count += 1

# Create a dictionary containing Apple conform ESP settings
# Create a dictionary containing client conform ESP settings
esp = {}
count = 1
for _, proposal in esp_proposals.items():
for _, proposal in esp_group['proposal'].items():
if {'encryption', 'hash'} <= set(proposal):
if proposal['encryption'] in set(vyos2client_cipher) and proposal['hash'] in set(vyos2client_integrity):
# We 're-code' from the VyOS IPsec proposals to the Apple naming scheme
proposal['encryption'] = vyos2client_cipher[ proposal['encryption'] ]
proposal['hash'] = vyos2client_integrity[ proposal['hash'] ]
# Copy PFS setting from the group, if present (we will need to
# transform this later once the IKE group is selected)
proposal['pfs'] = esp_group.get('pfs', 'enable')

esp.update( { str(count) : proposal } )
count += 1
Expand All @@ -230,21 +285,29 @@
tmp += f'({number}) Encryption {options["encryption"]}, Integrity {options["hash"]}, DH group {options["dh_group"]}\n'
tmp += '\nSelect one of the above IKE groups: '
data['ike_encryption'] = ike[ ask_input(tmp, valid_responses=list(ike)) ]
else:
elif len(ike) == 1:
data['ike_encryption'] = ike['1']
else:
exit(f'None of the configured IKE proposals for "{args.connection}" are supported by the client!')

if len(esp) > 1:
tmp = '\n'
for number, options in esp.items():
tmp += f'({number}) Encryption {options["encryption"]}, Integrity {options["hash"]}\n'
tmp += '\nSelect one of the above ESP groups: '
data['esp_encryption'] = esp[ ask_input(tmp, valid_responses=list(esp)) ]
else:
elif len(esp) == 1:
data['esp_encryption'] = esp['1']
else:
exit(f'None of the configured ESP proposals for "{args.connection}" are supported by the client!')

except KeyboardInterrupt:
exit("Interrupted")

# Transform the DH and PFS groups now that all selections are known
data['esp_encryption']['pfs'] = transform_pfs(data['esp_encryption']['pfs'], data['ike_encryption']['dh_group'])
data['ike_encryption']['dh_group'] = vyos2client_dh_group[ data['ike_encryption']['dh_group'] ]

print('\n\n==== <snip> ====')
if args.os == 'ios':
print(render_to_string('ipsec/ios_profile.j2', data))
Expand Down
Loading