Skip to content

Commit

Permalink
op-mode: T6424: ipsec: honor certificate CN and CA chain during profi…
Browse files Browse the repository at this point in the history
…le generation

In e6fe6e5 ("op-mode: ipsec: T6407: fix profile generation") we fixed
support for multiple CAs when dealing with the generation of Apple IOS profiles.

This commit extends support to properly include the common name of the server
certificate issuer and all it's paren't CAs. A list of parent CAs is
automatically generated from the "PKI" subsystem content and embedded into the
resulting profile.
  • Loading branch information
c-po committed Jun 9, 2024
1 parent a79c094 commit fba5644
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 26 deletions.
18 changes: 8 additions & 10 deletions data/templates/ipsec/ios_profile.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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 %}
Expand Down
13 changes: 10 additions & 3 deletions python/vyos/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
32 changes: 19 additions & 13 deletions src/op_mode/ikev2_profile_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
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
Expand Down Expand Up @@ -146,27 +150,29 @@
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)

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.
Expand Down

0 comments on commit fba5644

Please sign in to comment.