Add fixup enforcing SAN extension
Fixup to make sure that if we have a CN, we have a matching SAN entry. Change-Id: Ic37a053d909f2411e8f08acfa7cf9606a6316e58 Closes-bug: 1401580
This commit is contained in:
parent
2bd9477ea2
commit
81264fb9bb
@ -101,6 +101,15 @@ class X509Csr(signature.SignatureMixin):
|
|||||||
subject = ri['subject'][0]
|
subject = ri['subject'][0]
|
||||||
return name.X509Name(subject)
|
return name.X509Name(subject)
|
||||||
|
|
||||||
|
def set_subject(self, subject):
|
||||||
|
if not isinstance(subject, name.X509Name):
|
||||||
|
raise TypeError("subject must be an X509Name")
|
||||||
|
ri = self.get_request_info()
|
||||||
|
if ri['subject'] is None:
|
||||||
|
ri['subject'] = None
|
||||||
|
|
||||||
|
ri['subject'][0] = subject._name_obj
|
||||||
|
|
||||||
def get_attributes(self):
|
def get_attributes(self):
|
||||||
ri = self.get_request_info()
|
ri = self.get_request_info()
|
||||||
if ri['attributes'] is None:
|
if ri['attributes'] is None:
|
||||||
|
44
anchor/fixups.py
Normal file
44
anchor/fixups.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import netaddr
|
||||||
|
|
||||||
|
from anchor.X509 import extension
|
||||||
|
|
||||||
|
|
||||||
|
def enforce_alternative_names_present(csr=None, **kwargs):
|
||||||
|
"""Make sure that if CN is set, it's also present in SAN extension."""
|
||||||
|
sans = csr.get_extensions(extension.X509ExtensionSubjectAltName)
|
||||||
|
if sans:
|
||||||
|
san = sans[0]
|
||||||
|
else:
|
||||||
|
san = extension.X509ExtensionSubjectAltName()
|
||||||
|
|
||||||
|
san_updated = False
|
||||||
|
for cn in csr.get_subject_cn():
|
||||||
|
try:
|
||||||
|
ip = netaddr.IPAddress(cn)
|
||||||
|
if ip not in san.get_ips():
|
||||||
|
san.add_ip(ip)
|
||||||
|
san_updated = True
|
||||||
|
except netaddr.AddrFormatError:
|
||||||
|
if cn not in san.get_dns_ids():
|
||||||
|
san.add_dns_id(cn)
|
||||||
|
san_updated = True
|
||||||
|
|
||||||
|
if san_updated:
|
||||||
|
csr.add_extension(san)
|
||||||
|
return csr
|
@ -8,3 +8,12 @@ and the configuration.
|
|||||||
|
|
||||||
Unlike validators, each fixup has to return either a new CSR structure or the
|
Unlike validators, each fixup has to return either a new CSR structure or the
|
||||||
modified original.
|
modified original.
|
||||||
|
|
||||||
|
Included fixups
|
||||||
|
---------------
|
||||||
|
|
||||||
|
``enforce_alternative_names_present``
|
||||||
|
No parameters.
|
||||||
|
|
||||||
|
If the value from CN does not exist in subject alternative names, it will
|
||||||
|
be copied into either then DNS or IP field, depending on the format.
|
||||||
|
@ -49,6 +49,9 @@ anchor.authentication =
|
|||||||
ldap = anchor.auth.ldap:login
|
ldap = anchor.auth.ldap:login
|
||||||
static = anchor.auth.static:login
|
static = anchor.auth.static:login
|
||||||
|
|
||||||
|
anchor.fixups =
|
||||||
|
enforce_alternative_names_present = anchor.fixups:enforce_alternative_names_present
|
||||||
|
|
||||||
[files]
|
[files]
|
||||||
packages =
|
packages =
|
||||||
anchor
|
anchor
|
||||||
|
109
tests/fixups/test_fixup_ensure_alternative_names_present.py
Normal file
109
tests/fixups/test_fixup_ensure_alternative_names_present.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import netaddr
|
||||||
|
|
||||||
|
from anchor import fixups
|
||||||
|
from anchor.X509 import extension
|
||||||
|
from anchor.X509 import name
|
||||||
|
from anchor.X509 import signing_request
|
||||||
|
|
||||||
|
|
||||||
|
class TestEnsureAlternativeNamesPresent(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestEnsureAlternativeNamesPresent, self).setUp()
|
||||||
|
|
||||||
|
def _csr_with_cn(self, cn):
|
||||||
|
csr = signing_request.X509Csr()
|
||||||
|
subject = name.X509Name()
|
||||||
|
subject.add_name_entry(name.OID_commonName, cn)
|
||||||
|
csr.set_subject(subject)
|
||||||
|
return csr
|
||||||
|
|
||||||
|
def test_no_cn(self):
|
||||||
|
csr = signing_request.X509Csr()
|
||||||
|
subject = name.X509Name()
|
||||||
|
subject.add_name_entry(name.OID_localityName, "somewhere")
|
||||||
|
csr.set_subject(subject)
|
||||||
|
|
||||||
|
new_csr = fixups.enforce_alternative_names_present(csr=csr)
|
||||||
|
self.assertEqual(0, len(new_csr.get_extensions()))
|
||||||
|
|
||||||
|
def test_cn_only_ip(self):
|
||||||
|
csr = self._csr_with_cn("1.2.3.4")
|
||||||
|
|
||||||
|
new_csr = fixups.enforce_alternative_names_present(csr=csr)
|
||||||
|
self.assertEqual(1, len(new_csr.get_extensions()))
|
||||||
|
ext = new_csr.get_extensions(extension.X509ExtensionSubjectAltName)[0]
|
||||||
|
self.assertEqual([netaddr.IPAddress("1.2.3.4")], ext.get_ips())
|
||||||
|
|
||||||
|
def test_cn_only_dns(self):
|
||||||
|
csr = self._csr_with_cn("example.com")
|
||||||
|
|
||||||
|
new_csr = fixups.enforce_alternative_names_present(csr=csr)
|
||||||
|
self.assertEqual(1, len(new_csr.get_extensions()))
|
||||||
|
ext = new_csr.get_extensions(extension.X509ExtensionSubjectAltName)[0]
|
||||||
|
self.assertEqual(["example.com"], ext.get_dns_ids())
|
||||||
|
|
||||||
|
def test_cn_existing_ip(self):
|
||||||
|
csr = self._csr_with_cn("1.2.3.4")
|
||||||
|
san = extension.X509ExtensionSubjectAltName()
|
||||||
|
san.add_ip(netaddr.IPAddress("1.2.3.4"))
|
||||||
|
csr.add_extension(san)
|
||||||
|
|
||||||
|
new_csr = fixups.enforce_alternative_names_present(csr=csr)
|
||||||
|
self.assertEqual(1, len(new_csr.get_extensions()))
|
||||||
|
ext = new_csr.get_extensions(extension.X509ExtensionSubjectAltName)[0]
|
||||||
|
self.assertEqual([netaddr.IPAddress("1.2.3.4")], ext.get_ips())
|
||||||
|
|
||||||
|
def test_cn_existing_dns(self):
|
||||||
|
csr = self._csr_with_cn("example.com")
|
||||||
|
san = extension.X509ExtensionSubjectAltName()
|
||||||
|
san.add_dns_id("example.com")
|
||||||
|
csr.add_extension(san)
|
||||||
|
|
||||||
|
new_csr = fixups.enforce_alternative_names_present(csr=csr)
|
||||||
|
self.assertEqual(1, len(new_csr.get_extensions()))
|
||||||
|
ext = new_csr.get_extensions(extension.X509ExtensionSubjectAltName)[0]
|
||||||
|
self.assertEqual(["example.com"], ext.get_dns_ids())
|
||||||
|
|
||||||
|
def test_cn_extra_ip(self):
|
||||||
|
csr = self._csr_with_cn("1.2.3.4")
|
||||||
|
san = extension.X509ExtensionSubjectAltName()
|
||||||
|
san.add_ip(netaddr.IPAddress("2.3.4.5"))
|
||||||
|
csr.add_extension(san)
|
||||||
|
|
||||||
|
new_csr = fixups.enforce_alternative_names_present(csr=csr)
|
||||||
|
self.assertEqual(1, len(new_csr.get_extensions()))
|
||||||
|
ext = new_csr.get_extensions(extension.X509ExtensionSubjectAltName)[0]
|
||||||
|
ips = ext.get_ips()
|
||||||
|
self.assertIn(netaddr.IPAddress("1.2.3.4"), ips)
|
||||||
|
self.assertIn(netaddr.IPAddress("2.3.4.5"), ips)
|
||||||
|
|
||||||
|
def test_cn_extra_dns(self):
|
||||||
|
csr = self._csr_with_cn("example.com")
|
||||||
|
san = extension.X509ExtensionSubjectAltName()
|
||||||
|
san.add_dns_id("other.example.com")
|
||||||
|
csr.add_extension(san)
|
||||||
|
|
||||||
|
new_csr = fixups.enforce_alternative_names_present(csr=csr)
|
||||||
|
self.assertEqual(1, len(new_csr.get_extensions()))
|
||||||
|
ext = new_csr.get_extensions(extension.X509ExtensionSubjectAltName)[0]
|
||||||
|
ids = ext.get_dns_ids()
|
||||||
|
self.assertIn("example.com", ids)
|
||||||
|
self.assertIn("other.example.com", ids)
|
Loading…
x
Reference in New Issue
Block a user