summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2021-07-29 18:47:42 +0200
committerChristian Poessinger <christian@poessinger.com>2021-07-29 18:48:10 +0200
commit65765fe95a34d81ad4a3aedb035936bbaf6a3f0e (patch)
treeb8780d35c1dddb706ec60b299dd74603deae3690
parent417b46fb11061615205038745695e84d48055979 (diff)
downloadvyos-1x-65765fe95a34d81ad4a3aedb035936bbaf6a3f0e.tar.gz
vyos-1x-65765fe95a34d81ad4a3aedb035936bbaf6a3f0e.zip
ipsec: T1210: add op-mode command to print Windows connection profile
-rw-r--r--data/templates/ipsec/windows_profile.tmpl4
-rw-r--r--op-mode-definitions/generate-ipsec-profile.xml.in111
-rwxr-xr-xsrc/op_mode/ikev2_profile_generator.py201
3 files changed, 222 insertions, 94 deletions
diff --git a/data/templates/ipsec/windows_profile.tmpl b/data/templates/ipsec/windows_profile.tmpl
new file mode 100644
index 000000000..8c26944be
--- /dev/null
+++ b/data/templates/ipsec/windows_profile.tmpl
@@ -0,0 +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
diff --git a/op-mode-definitions/generate-ipsec-profile.xml.in b/op-mode-definitions/generate-ipsec-profile.xml.in
index d1e5efd20..be9227971 100644
--- a/op-mode-definitions/generate-ipsec-profile.xml.in
+++ b/op-mode-definitions/generate-ipsec-profile.xml.in
@@ -7,33 +7,49 @@
<help>Generate IPsec related configurations</help>
</properties>
<children>
- <tagNode name="mac-ios-profile">
+ <node name="profile">
<properties>
- <help>Generate Apple iOS profile from IPsec connection profile</help>
- <completionHelp>
- <path>vpn ipsec remote-access connection</path>
- </completionHelp>
+ <help>Generate IKEv2 IPSec remote-access VPN profiles</help>
</properties>
<children>
- <tagNode name="remote">
+ <tagNode name="ios-remote-access">
<properties>
- <help>Remote address where the client will connect to</help>
+ <help>Generate iOS profile for specified remote-access connection name</help>
<completionHelp>
- <list>&lt;fqdn&gt;</list>
- <script>${vyos_completion_dir}/list_local_ips.sh --both</script>
+ <path>vpn ipsec remote-access connection</path>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --connection "$4" --remote "$6"</command>
<children>
- <tagNode name="name">
+ <tagNode name="remote">
<properties>
- <help>Connection name as seen in the VPN application</help>
+ <help>Remote address where the client will connect to</help>
<completionHelp>
- <list>&lt;name&gt;</list>
+ <list>&lt;fqdn&gt;</list>
+ <script>${vyos_completion_dir}/list_local_ips.sh --both</script>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --connection "$4" --remote "$6" --name "$8"</command>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7"</command>
<children>
+ <tagNode name="name">
+ <properties>
+ <help>Connection name as seen in the VPN application</help>
+ <completionHelp>
+ <list>&lt;name&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --name "$9"</command>
+ <children>
+ <tagNode name="profile">
+ <properties>
+ <help>Profile name as seen under system profiles</help>
+ <completionHelp>
+ <list>&lt;name&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --name "$9" --profile "${11}"</command>
+ </tagNode>
+ </children>
+ </tagNode>
<tagNode name="profile">
<properties>
<help>Profile name as seen under system profiles</help>
@@ -41,18 +57,40 @@
<list>&lt;name&gt;</list>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --connection "$4" --remote "$6" --name "$8" --profile "${10}"</command>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --profile "$9"</command>
+ <children>
+ <tagNode name="name">
+ <properties>
+ <help>Connection name as seen in the VPN application</help>
+ <completionHelp>
+ <list>&lt;name&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --profile "$9" --name "${11}"</command>
+ </tagNode>
+ </children>
</tagNode>
</children>
</tagNode>
- <tagNode name="profile">
+ </children>
+ </tagNode>
+ <tagNode name="windows-remote-access">
+ <properties>
+ <help>Generate iOS profile for specified remote-access connection name</help>
+ <completionHelp>
+ <path>vpn ipsec remote-access connection</path>
+ </completionHelp>
+ </properties>
+ <children>
+ <tagNode name="remote">
<properties>
- <help>Profile name as seen under system profiles</help>
+ <help>Remote address where the client will connect to</help>
<completionHelp>
- <list>&lt;name&gt;</list>
+ <list>&lt;fqdn&gt;</list>
+ <script>${vyos_completion_dir}/list_local_ips.sh --both</script>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --connection "$4" --remote "$6" --profile "$8"</command>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7"</command>
<children>
<tagNode name="name">
<properties>
@@ -61,14 +99,45 @@
<list>&lt;name&gt;</list>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --connection "$4" --remote "$6" --profile "$8" --name "${10}"</command>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --name "$9"</command>
+ <children>
+ <tagNode name="profile">
+ <properties>
+ <help>Profile name as seen under system profiles</help>
+ <completionHelp>
+ <list>&lt;name&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --name "$9" --profile "${11}"</command>
+ </tagNode>
+ </children>
+ </tagNode>
+ <tagNode name="profile">
+ <properties>
+ <help>Profile name as seen under system profiles</help>
+ <completionHelp>
+ <list>&lt;name&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --profile "$9"</command>
+ <children>
+ <tagNode name="name">
+ <properties>
+ <help>Connection name as seen in the VPN application</help>
+ <completionHelp>
+ <list>&lt;name&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --profile "$9" --name "${11}"</command>
+ </tagNode>
+ </children>
</tagNode>
</children>
</tagNode>
</children>
</tagNode>
</children>
- </tagNode>
+ </node>
</children>
</node>
</children>
diff --git a/src/op_mode/ikev2_profile_generator.py b/src/op_mode/ikev2_profile_generator.py
index ce93ec057..d45525431 100755
--- a/src/op_mode/ikev2_profile_generator.py
+++ b/src/op_mode/ikev2_profile_generator.py
@@ -26,54 +26,6 @@ from vyos.pki import load_certificate
from vyos.template import render_to_string
from vyos.util import ask_input
-parser = argparse.ArgumentParser()
-parser.add_argument("--connection", action="store", help="IPsec IKEv2 remote-access connection name from CLI", required=True)
-parser.add_argument("--remote", action="store", help="VPN connection remote-address where the client will connect to", required=True)
-parser.add_argument("--profile", action="store", help="IKEv2 profile name used in the profile list on the device")
-parser.add_argument("--name", action="store", help="VPN connection name as seen in the VPN application later")
-args = parser.parse_args()
-
-ipsec_base = ['vpn', 'ipsec']
-config_base = ipsec_base + ['remote-access', 'connection']
-pki_base = ['pki']
-conf = Config()
-if not conf.exists(config_base):
- exit('IPSec remote-access is not configured!')
-
-profile_name = 'VyOS IKEv2 Profile'
-if args.profile:
- profile_name = args.profile
-
-vpn_name = 'VyOS IKEv2 Profile'
-if args.name:
- vpn_name = args.name
-
-conn_base = config_base + [args.connection]
-if not conf.exists(conn_base):
- exit(f'IPSec remote-access connection "{args.connection}" does not exist!')
-
-data = conf.get_config_dict(conn_base, key_mangling=('-', '_'),
- get_first_key=True, no_tag_node_value_mangle=True)
-
-data['profile_name'] = profile_name
-data['vpn_name'] = vpn_name
-data['remote'] = args.remote
-# This is a reverse-DNS style unique identifier used to detect duplicate profiles
-tmp = getfqdn().split('.')
-tmp = reversed(tmp)
-data['rfqdn'] = '.'.join(tmp)
-
-pki = conf.get_config_dict(pki_base, get_first_key=True)
-ca_name = data['authentication']['x509']['ca_certificate']
-cert_name = data['authentication']['x509']['certificate']
-
-ca_cert = load_certificate(pki['ca'][ca_name]['certificate'])
-cert = load_certificate(pki['certificate'][cert_name]['certificate'])
-
-data['ca_cn'] = ca_cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
-data['cert_cn'] = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
-data['ca_cert'] = conf.return_value(pki_base + ['ca', ca_name, 'certificate'])
-
# Apple profiles only support one IKE/ESP encryption cipher and hash, whereas
# VyOS comes with a multitude of different proposals for a connection.
#
@@ -99,6 +51,25 @@ vyos2apple_cipher = {
'chacha20poly1305' : 'ChaCha20Poly1305',
}
+# Windows supports IKE-SA encryption algorithms:
+# - DES3
+# - AES128
+# - AES192
+# - AES256
+# - GCMAES128
+# - GCMAES192
+# - GCMAES256
+#
+vyos2windows_cipher = {
+ '3des' : 'DES3',
+ 'aes128' : 'AES128',
+ 'aes192' : 'AES192',
+ 'aes256' : 'AES256',
+ 'aes128gcm128' : 'GCMAES128',
+ 'aes192gcm128' : 'GCMAES192',
+ 'aes256gcm128' : 'GCMAES256',
+}
+
# IOS supports IKE-SA integrity algorithms:
# - SHA1-96
# - SHA1-160
@@ -114,27 +85,102 @@ vyos2apple_integrity = {
'sha512' : 'SHA2-512',
}
+# Windows supports IKE-SA integrity algorithms:
+# - SHA1-96
+# - SHA1-160
+# - SHA2-256
+# - SHA2-384
+# - SHA2-512
+#
+vyos2windows_integrity = {
+ 'sha1' : 'SHA196',
+ 'sha256' : 'SHA256',
+ 'aes128gmac' : 'GCMAES128',
+ 'aes192gmac' : 'GCMAES192',
+ 'aes256gmac' : 'GCMAES256',
+}
+
# 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
-supported_dh_groups = ['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']
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--os', const='all', nargs='?', choices=['ios', 'windows'], help='Operating system used for config generation', required=True)
+parser.add_argument("--connection", action="store", help='IPsec IKEv2 remote-access connection name from CLI', required=True)
+parser.add_argument("--remote", action="store", help='VPN connection remote-address where the client will connect to', required=True)
+parser.add_argument("--profile", action="store", help='IKEv2 profile name used in the profile list on the device')
+parser.add_argument("--name", action="store", help='VPN connection name as seen in the VPN application later')
+args = parser.parse_args()
+
+ipsec_base = ['vpn', 'ipsec']
+config_base = ipsec_base + ['remote-access', 'connection']
+pki_base = ['pki']
+conf = Config()
+if not conf.exists(config_base):
+ exit('IPSec remote-access is not configured!')
+
+profile_name = 'VyOS IKEv2 Profile'
+if args.profile:
+ profile_name = args.profile
+
+vpn_name = 'VyOS IKEv2 VPN'
+if args.name:
+ vpn_name = args.name
+
+conn_base = config_base + [args.connection]
+if not conf.exists(conn_base):
+ exit(f'IPSec remote-access connection "{args.connection}" does not exist!')
+
+data = conf.get_config_dict(conn_base, key_mangling=('-', '_'),
+ get_first_key=True, no_tag_node_value_mangle=True)
+
+data['profile_name'] = profile_name
+data['vpn_name'] = vpn_name
+data['remote'] = args.remote
+# This is a reverse-DNS style unique identifier used to detect duplicate profiles
+tmp = getfqdn().split('.')
+tmp = reversed(tmp)
+data['rfqdn'] = '.'.join(tmp)
+
+pki = conf.get_config_dict(pki_base, get_first_key=True)
+ca_name = data['authentication']['x509']['ca_certificate']
+cert_name = data['authentication']['x509']['certificate']
+
+ca_cert = load_certificate(pki['ca'][ca_name]['certificate'])
+cert = load_certificate(pki['certificate'][cert_name]['certificate'])
+
+data['ca_cn'] = ca_cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
+data['cert_cn'] = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
+data['ca_cert'] = conf.return_value(pki_base + ['ca', ca_name, 'certificate'])
esp_proposals = conf.get_config_dict(ipsec_base + ['esp-group', data['esp_group'], 'proposal'],
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)
-# Create a dictionary containing Apple conform IKE settings
+
+# This script works only for Apple iOS/iPadOS and Windows. Both operating systems
+# have different limitations thus we load the limitations based on the operating
+# system used.
+
+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;
+
+# Create a dictionary containing client conform IKE settings
ike = {}
count = 1
for _, proposal in ike_proposal.items():
if {'dh_group', 'encryption', 'hash'} <= set(proposal):
- if (proposal['encryption'] in set(vyos2apple_cipher) and
- proposal['hash'] in set(vyos2apple_integrity) and
+ if (proposal['encryption'] in set(vyos2client_cipher) and
+ proposal['hash'] in set(vyos2client_integrity) and
proposal['dh_group'] in set(supported_dh_groups)):
# We 're-code' from the VyOS IPSec proposals to the Apple naming scheme
- proposal['encryption'] = vyos2apple_cipher[ proposal['encryption'] ]
- proposal['hash'] = vyos2apple_integrity[ proposal['hash'] ]
+ proposal['encryption'] = vyos2client_cipher[ proposal['encryption'] ]
+ proposal['hash'] = vyos2client_integrity[ proposal['hash'] ]
ike.update( { str(count) : proposal } )
count += 1
@@ -144,32 +190,41 @@ esp = {}
count = 1
for _, proposal in esp_proposals.items():
if {'encryption', 'hash'} <= set(proposal):
- if proposal['encryption'] in set(vyos2apple_cipher) and proposal['hash'] in set(vyos2apple_integrity):
+ 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'] = vyos2apple_cipher[ proposal['encryption'] ]
- proposal['hash'] = vyos2apple_integrity[ proposal['hash'] ]
+ proposal['encryption'] = vyos2client_cipher[ proposal['encryption'] ]
+ proposal['hash'] = vyos2client_integrity[ proposal['hash'] ]
esp.update( { str(count) : proposal } )
count += 1
try:
- # Propare the input questions for the user
- tmp = '\n'
- for number, options in ike.items():
- 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)) ]
-
- 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)) ]
-
+ if len(ike) > 1:
+ # Propare the input questions for the user
+ tmp = '\n'
+ for number, options in ike.items():
+ 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:
+ data['ike_encryption'] = ike['1']
+
+ 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:
+ data['esp_encryption'] = esp['1']
except KeyboardInterrupt:
exit("Interrupted")
print('\n\n==== <snip> ====')
-print(render_to_string('ipsec/ios_profile.tmpl', data))
-print('==== </snip> ====\n')
-print('Save the XML from above to a new file named "vyos.mobileconfig" and E-Mail it to your phone.')
+if args.os == 'ios':
+ print(render_to_string('ipsec/ios_profile.tmpl', data))
+ print('==== </snip> ====\n')
+ print('Save the XML from above to a new file named "vyos.mobileconfig" and E-Mail it to your phone.')
+elif args.os == 'windows':
+ print(render_to_string('ipsec/windows_profile.tmpl', data))
+ print('==== </snip> ====\n')