diff options
author | Christian Breunig <christian@breunig.cc> | 2024-06-10 10:27:49 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-10 10:27:49 +0200 |
commit | 82607438d6df5291c581d802c7a2a98eabe084ff (patch) | |
tree | 3d70559d26469951d4a5b3674d8567be337f6f09 | |
parent | 7ade4fa71b535289577978a73746c1fc1d993803 (diff) | |
parent | 4e51569013b3f78abea9c18e5a6ecb9ff5ae4687 (diff) | |
download | vyos-1x-82607438d6df5291c581d802c7a2a98eabe084ff.tar.gz vyos-1x-82607438d6df5291c581d802c7a2a98eabe084ff.zip |
Merge pull request #3610 from c-po/ipsec-profile-T6424
op-mode: T6424: ipsec: honor certificate CN and CA chain during profile generation
-rw-r--r-- | data/templates/ipsec/ios_profile.j2 | 18 | ||||
-rw-r--r-- | python/vyos/template.py | 13 | ||||
-rwxr-xr-x | src/op_mode/ikev2_profile_generator.py | 36 |
3 files changed, 41 insertions, 26 deletions
diff --git a/data/templates/ipsec/ios_profile.j2 b/data/templates/ipsec/ios_profile.j2 index a9ae1c7a9..935acbf8e 100644 --- a/data/templates/ipsec/ios_profile.j2 +++ b/data/templates/ipsec/ios_profile.j2 @@ -48,10 +48,10 @@ <!-- Optional, if it matches the CN of the root CA certificate (not the full subject DN) a certificate request will be sent NOTE: If this is not configured make sure to configure leftsendcert=always on the server, otherwise it won't send its certificate --> <key>ServerCertificateIssuerCommonName</key> - <string>{{ ca_cn }}</string> + <string>{{ ca_common_name }}</string> <!-- Optional, the CN or one of the subjectAltNames of the server certificate to verify it, if not set RemoteIdentifier will be used --> <key>ServerCertificateCommonName</key> - <string>{{ cert_cn }}</string> + <string>{{ cert_common_name }}</string> <!-- The server is authenticated using a certificate --> <key>AuthenticationMethod</key> <string>Certificate</string> @@ -83,24 +83,22 @@ </dict> </dict> </dict> -{% if certs is vyos_defined %} +{% if ca_certificates is vyos_defined %} <!-- This payload is optional but it provides an easy way to install the CA certificate together with the configuration --> -{% for cert in certs %} - <!-- Payload for: {{ cert.ca_cn }} --> +{% for ca in ca_certificates %} + <!-- Payload for: {{ ca.ca_name }} --> <dict> <key>PayloadIdentifier</key> - <string>org.{{ cert.ca_cn | lower | replace(' ', '.') | replace('_', '.') }}</string> + <string>org.{{ ca.ca_name | lower | replace(' ', '.') | replace('_', '.') }}</string> <key>PayloadUUID</key> - <string>{{ cert.ca_cn | generate_uuid4 }}</string> + <string>{{ ca.ca_name | get_uuid }}</string> <key>PayloadType</key> <string>com.apple.security.root</string> <key>PayloadVersion</key> <integer>1</integer> <!-- This is the Base64 (PEM) encoded CA certificate --> <key>PayloadContent</key> - <data> - {{ cert.ca_cert }} - </data> + <data>{{ ca.ca_chain }}</data> </dict> {% endfor %} {% endif %} diff --git a/python/vyos/template.py b/python/vyos/template.py index fbc5f1456..e8d7ba669 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -525,10 +525,17 @@ def get_esp_ike_cipher(group_config, ike_group=None): return ciphers @register_filter('get_uuid') -def get_uuid(interface): +def get_uuid(seed): """ Get interface IP addresses""" - from uuid import uuid1 - return uuid1() + if seed: + from hashlib import md5 + from uuid import UUID + tmp = md5() + tmp.update(seed.encode('utf-8')) + return str(UUID(tmp.hexdigest())) + else: + from uuid import uuid1 + return uuid1() openvpn_translate = { 'des': 'des-cbc', diff --git a/src/op_mode/ikev2_profile_generator.py b/src/op_mode/ikev2_profile_generator.py index 4ac4fb14a..169a15840 100755 --- a/src/op_mode/ikev2_profile_generator.py +++ b/src/op_mode/ikev2_profile_generator.py @@ -21,6 +21,10 @@ from socket import getfqdn from cryptography.x509.oid import NameOID from vyos.configquery import ConfigTreeQuery +from vyos.pki import CERT_BEGIN +from vyos.pki import CERT_END +from vyos.pki import find_chain +from vyos.pki import encode_certificate from vyos.pki import load_certificate from vyos.template import render_to_string from vyos.utils.io import ask_input @@ -146,27 +150,33 @@ data['rfqdn'] = '.'.join(tmp) pki = conf.get_config_dict(pki_base, get_first_key=True) cert_name = data['authentication']['x509']['certificate'] -data['certs'] = [] +cert_data = load_certificate(pki['certificate'][cert_name]['certificate']) +data['cert_common_name'] = cert_data.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value +data['ca_common_name'] = cert_data.issuer.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value +data['ca_certificates'] = [] -for ca_name in data['authentication']['x509']['ca_certificate']: - tmp = {} - ca_cert = load_certificate(pki['ca'][ca_name]['certificate']) - cert = load_certificate(pki['certificate'][cert_name]['certificate']) - - - tmp['ca_cn'] = ca_cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value - tmp['cert_cn'] = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value - tmp['ca_cert'] = conf.value(pki_base + ['ca', ca_name, 'certificate']) - - data['certs'].append(tmp) +loaded_ca_certs = {load_certificate(c['certificate']) + for c in pki['ca'].values()} if 'ca' in pki else {} +for ca_name in data['authentication']['x509']['ca_certificate']: + loaded_ca_cert = load_certificate(pki['ca'][ca_name]['certificate']) + ca_full_chain = find_chain(loaded_ca_cert, loaded_ca_certs) + for ca in ca_full_chain: + tmp = { + 'ca_name' : ca.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value, + 'ca_chain' : encode_certificate(ca).replace(CERT_BEGIN, '').replace(CERT_END, '').replace('\n', ''), + } + data['ca_certificates'].append(tmp) + +# Remove duplicate list entries for CA certificates, as they are added by their common name +# 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'], 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) - # 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. |