summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Sulfrian <asulfrian@zedat.fu-berlin.de>2022-02-14 18:36:42 +0100
committerAlexander Sulfrian <asulfrian@zedat.fu-berlin.de>2022-02-14 18:54:15 +0100
commita675ab70d1444c13a8c39eab977fdea8e9d6cd94 (patch)
treeef3d54239507e9932da0a24a08a6b0521ab69634
parent2132d4f7dea1e7355702ca096ff88628c4174bca (diff)
downloadbcfg2-a675ab70d1444c13a8c39eab977fdea8e9d6cd94.tar.gz
bcfg2-a675ab70d1444c13a8c39eab977fdea8e9d6cd94.tar.bz2
bcfg2-a675ab70d1444c13a8c39eab977fdea8e9d6cd94.zip
SSLCA: Add generator for custom cert/key formats
This generator will not generate a new ssl key or ssl cert, but it will generate a custom format of already existing ssl keys and certs.
-rw-r--r--schemas/sslca-format.xsd150
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgSSLCAFormatCreator.py116
2 files changed, 266 insertions, 0 deletions
diff --git a/schemas/sslca-format.xsd b/schemas/sslca-format.xsd
new file mode 100644
index 000000000..9f11dc847
--- /dev/null
+++ b/schemas/sslca-format.xsd
@@ -0,0 +1,150 @@
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:py="http://genshi.edgewall.org/" xml:lang="en">
+ <xsd:annotation>
+ <xsd:documentation>
+ Schema for :ref:`server-plugins-generators-cfg-ssl-certificates`
+ ``sslformat.xml``
+ </xsd:documentation>
+ </xsd:annotation>
+
+ <xsd:import namespace="http://genshi.edgewall.org/"
+ schemaLocation="genshi.xsd"/>
+
+ <xsd:complexType name="SSLCAFormatGroupType">
+ <xsd:annotation>
+ <xsd:documentation>
+ An **SSLCAFormatGroupType** is a tag used to provide logic.
+ Child entries of an SSLCAFormatGroupType tag only apply to
+ machines that match the condition specified -- either
+ membership in a group, or a matching client name.
+ :xml:attribute:`SSLCAFormatGroupType:negate` can be set to negate
+ the sense of the match.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:choice minOccurs="1" maxOccurs="unbounded">
+ <xsd:group ref="py:genshiElements"/>
+ <xsd:element name="Group" type="SSLCAFormatGroupType"/>
+ <xsd:element name="Client" type="SSLCAFormatGroupType"/>
+ <xsd:element name="Cert" type="CertFormatType"/>
+ <xsd:element name="Key" type="KeyFormatType"/>
+ <xsd:element name="Format" type="FormatType"/>
+ </xsd:choice>
+ <xsd:attribute name='name' type='xsd:string'>
+ <xsd:annotation>
+ <xsd:documentation>
+ The name of the client or group to match on. Child entries
+ will only apply to this client or group (unless
+ :xml:attribute:`SSLCAFormatGroupType:negate` is set).
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name='negate' type='xsd:boolean'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Negate the sense of the match, so that child entries only
+ apply to a client if it is not a member of the given group
+ or does not have the given name.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
+ </xsd:complexType>
+
+ <xsd:simpleType name="CertFormatTypeEnum">
+ <xsd:annotation>
+ <xsd:documentation>
+ Available cert formats
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="pem"/>
+ <xsd:enumeration value="der"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="KeyFormatTypeEnum">
+ <xsd:annotation>
+ <xsd:documentation>
+ Available ker formats
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="pem"/>
+ <xsd:enumeration value="der"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:complexType name="CertFormatType">
+ <xsd:attribute type="CertFormatTypeEnum" name="format" default='pem'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Format of the cert in the generated format. Currently only ``pem``
+ and ``der`` is supported.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
+ </xsd:complexType>
+
+ <xsd:complexType name="KeyFormatType">
+ <xsd:attribute type="KeyFormatTypeEnum" name="format" default='pem'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Format of the key in the generated format. Currently only ``pem``
+ and ``der`` is supported.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
+ </xsd:complexType>
+
+ <xsd:complexType name="FormatType">
+ <xsd:choice minOccurs="1" maxOccurs="unbounded">
+ <xsd:group ref="py:genshiElements"/>
+ <xsd:element name="Group" type="SSLCAFormatGroupType"/>
+ <xsd:element name="Client" type="SSLCAFormatGroupType"/>
+ <xsd:element name="Cert" type="CertFormatType"/>
+ <xsd:element name="Key" type="KeyFormatType"/>
+ <xsd:element name="Format" type="FormatType"/>
+ </xsd:choice>
+ <xsd:attribute name="cert" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation>
+ The full path to the cert entry to use for this format.
+ This is the *client* path; e.g., for a cert defined at
+ ``/var/lib/bcfg2/SSLCA/etc/pki/tls/private/foo.pem/sslcert.xml``,
+ **cert** should be ``/etc/pki/tls/private/foo.pem``. This
+ if required if the cert is used in the format.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="key" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation>
+ The full path to the key entry to use for this certificate.
+ This is the *client* path; e.g., for a key defined at
+ ``/var/lib/bcfg2/SSLCA/etc/pki/tls/private/foo.key/sslkey.xml``,
+ **key** should be ``/etc/pki/tls/private/foo.key``. This is
+ only required if the key is used in the format and **cert**
+ is not a SSLCA generated cert.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+
+ <xsd:complexType name="FormatInfoType">
+ <xsd:annotation>
+ <xsd:documentation>
+ Top-level tag for describing an SSLCA generated cert format.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:choice minOccurs="1" maxOccurs="unbounded">
+ <xsd:group ref="py:genshiElements"/>
+ <xsd:element name="Group" type="SSLCAFormatGroupType"/>
+ <xsd:element name="Client" type="SSLCAFormatGroupType"/>
+ <xsd:element name="Format" type="FormatType"/>
+ </xsd:choice>
+ </xsd:complexType>
+
+ <xsd:element name="FormatInfo" type="FormatInfoType"/>
+</xsd:schema>
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgSSLCAFormatCreator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgSSLCAFormatCreator.py
new file mode 100644
index 000000000..3dadf0345
--- /dev/null
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgSSLCAFormatCreator.py
@@ -0,0 +1,116 @@
+""" Cfg creator that creates SSL certs """
+
+import lxml.etree
+from Bcfg2.Utils import Executor
+from Bcfg2.Server.FileMonitor import get_fam
+from Bcfg2.Server.Plugin import PluginExecutionError
+from Bcfg2.Server.Plugins.Cfg import CfgCreationError, XMLCfgCreator, \
+ CfgCreator
+
+
+class CfgSSLCAFormatCreator(XMLCfgCreator):
+ """ This class acts as both a Cfg creator that creates formatted
+ SSL certs."""
+
+ #: Different configurations for different clients/groups can be
+ #: handled with Client and Group tags within pubkey.xml
+ __specific__ = False
+
+ #: Handle XML specifications of private keys
+ __basenames__ = ['sslformat.xml']
+
+ def __init__(self, fname):
+ XMLCfgCreator.__init__(self, fname)
+ self.cmd = Executor()
+
+ def create_data(self, entry, metadata):
+ """ generate a new formatted cert """
+ self.logger.info("Cfg: Generating formatted SSL cert for %s" %
+ self.name)
+ elem = self.XMLMatch(metadata).find("Format")
+ certfile = None
+ keyfile = None
+
+ data = ''
+ for part in elem:
+ if part.tag == 'Key':
+ if keyfile is None:
+ keyfile = self._get_keyfile(elem, metadata)
+
+ cmd = ["openssl", "rsa", "-in", keyfile]
+ if part.get('format') == 'der':
+ cmd.extend(['-outform', 'DER'])
+ result = self.cmd.run(cmd)
+ data += result.stdout
+ elif part.tag == 'Cert':
+ if certfile is None:
+ certfile = self._get_certfile(elem, metadata)
+
+ cmd = ["openssl", "x509", "-in", certfile]
+ if part.get('format') == 'der':
+ cmd.exend(['-outform', 'DER'])
+ result = self.cmd.run(cmd)
+ data += result.stdout
+ else:
+ raise CfgCreationError("Cfg: Unknown SSL Cert format "
+ "%s for %s" % (part.tag, self.name))
+ self.write_data(data, **self.get_specificity(metadata))
+ return data
+
+ def _get_keyfile(self, elem, metadata):
+ """ Given a <Format/> element and client metadata, return the
+ full path to the file on the filesystem that the key lives in."""
+ keypath = elem.get("key", None)
+ if keypath is not None:
+ eset = self.cfg.entries[keypath]
+ try:
+ return eset.best_matching(metadata).name
+ except PluginExecutionError:
+ raise CfgCreationError("Cfg: No SSL Key found at %s" %
+ keypath)
+ else:
+ # Get ssl key from cert creator
+ certpath = elem.get("cert")
+ eset = self.cfg.entries[certpath]
+ try:
+ creator = eset.best_matching(metadata,
+ eset.get_handlers(metadata,
+ CfgCreator))
+ except PluginExecutionError:
+ raise CfgCreationError("Cfg: No SSL cert creator defined "
+ "for %s" % certpath)
+
+ cert = creator.XMLMatch(metadata).find("Cert")
+ return creator._get_keyfile(cert, metadata)
+
+ def _get_certfile(self, elem, metadata):
+ """ Given a <Format/> element and client metadata, return the
+ full path to the file on the filesystem that the cert lives in."""
+ certpath = elem.get("cert")
+ eset = self.cfg.entries[certpath]
+ try:
+ return eset.best_matching(metadata).name
+ except PluginExecutionError:
+ # SSL cert needs to be created
+ try:
+ creator = eset.best_matching(metadata,
+ eset.get_handlers(metadata,
+ CfgCreator))
+ except PluginExecutionError:
+ raise CfgCreationError("Cfg: No SSL Cert or cert creator "
+ "defined for %s" % certpath)
+
+ certentry = lxml.etree.Element("Path", name=certpath)
+ creator.create_data(certentry, metadata)
+
+ tries = 0
+ while True:
+ if tries >= 10:
+ raise CfgCreationError("Cfg: Timed out waiting for event "
+ "on SSL cert at %s" % certpath)
+ get_fam().handle_events_in_interval(1)
+ try:
+ return eset.best_matching(metadata).name
+ except PluginExecutionError:
+ tries += 1
+ continue