Skip to content

Commit

Permalink
T6617: T6618: vpn ipsec remote-access: fix profile generators
Browse files Browse the repository at this point in the history
(cherry picked from commit e97d86e)
  • Loading branch information
lucasec authored and mergify[bot] committed Aug 1, 2024
1 parent e61a175 commit ca12e0b
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 13 deletions.
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

0 comments on commit ca12e0b

Please sign in to comment.