diff options
Diffstat (limited to 'library/X509/ProvidedHook')
-rw-r--r-- | library/X509/ProvidedHook/HostsImportSource.php | 70 | ||||
-rw-r--r-- | library/X509/ProvidedHook/ServicesImportSource.php | 85 | ||||
-rw-r--r-- | library/X509/ProvidedHook/x509ImportSource.php | 49 |
3 files changed, 204 insertions, 0 deletions
diff --git a/library/X509/ProvidedHook/HostsImportSource.php b/library/X509/ProvidedHook/HostsImportSource.php new file mode 100644 index 0000000..6f7cfb3 --- /dev/null +++ b/library/X509/ProvidedHook/HostsImportSource.php @@ -0,0 +1,70 @@ +<?php +// Icinga Web 2 X.509 Module | (c) 2019 Icinga GmbH | GPLv2 + +namespace Icinga\Module\X509\ProvidedHook; + +use ipl\Sql; + +class HostsImportSource extends x509ImportSource +{ + public function fetchData() + { + $targets = (new Sql\Select()) + ->from('x509_target t') + ->columns([ + 'host_ip' => 't.ip', + 'host_name' => 't.hostname', + 'host_ports' => 'GROUP_CONCAT(DISTINCT t.port SEPARATOR ",")' + ]) + ->join('x509_certificate_chain cc', 'cc.id = t.latest_certificate_chain_id') + ->join('x509_certificate_chain_link ccl', 'ccl.certificate_chain_id = cc.id') + ->join('x509_certificate c', 'c.id = ccl.certificate_id') + ->where(['ccl.order = ?' => 0]) + ->groupBy(['t.ip', 't.hostname']); + + $results = []; + $foundDupes = []; + foreach ($this->getDb()->select($targets) as $target) { + list($ipv4, $ipv6) = $this->transformIpAddress($target->host_ip); + $target->host_ip = $ipv4 ?: $ipv6; + $target->host_address = $ipv4; + $target->host_address6 = $ipv6; + + if (isset($foundDupes[$target->host_name])) { + // For load balanced systems the IP address is the better choice + $target->host_name_or_ip = $target->host_ip; + } elseif (! isset($results[$target->host_name])) { + // Hostnames are usually preferred, especially in the case of SNI + $target->host_name_or_ip = $target->host_name; + } else { + $dupe = $results[$target->host_name]; + unset($results[$target->host_name]); + $foundDupes[$dupe->host_name] = true; + $dupe->host_name_or_ip = $dupe->host_ip; + $results[$dupe->host_name_or_ip] = $dupe; + $target->host_name_or_ip = $target->host_ip; + } + + $results[$target->host_name_or_ip] = $target; + } + + return $results; + } + + public function listColumns() + { + return [ + 'host_name_or_ip', + 'host_ip', + 'host_name', + 'host_ports', + 'host_address', + 'host_address6' + ]; + } + + public static function getDefaultKeyColumnName() + { + return 'host_name_or_ip'; + } +} diff --git a/library/X509/ProvidedHook/ServicesImportSource.php b/library/X509/ProvidedHook/ServicesImportSource.php new file mode 100644 index 0000000..19f9de9 --- /dev/null +++ b/library/X509/ProvidedHook/ServicesImportSource.php @@ -0,0 +1,85 @@ +<?php +// Icinga Web 2 X.509 Module | (c) 2019 Icinga GmbH | GPLv2 + +namespace Icinga\Module\X509\ProvidedHook; + +use ipl\Sql; + +class ServicesImportSource extends x509ImportSource +{ + public function fetchData() + { + $targets = (new Sql\Select()) + ->from('x509_target t') + ->columns([ + 'host_ip' => 't.ip', + 'host_name' => 't.hostname', + 'host_port' => 't.port', + 'cert_subject' => 'c.subject', + 'cert_issuer' => 'c.issuer', + 'cert_self_signed' => 'COALESCE(ci.self_signed, c.self_signed)', + 'cert_trusted' => 'c.trusted', + 'cert_valid_from' => 'c.valid_from', + 'cert_valid_to' => 'c.valid_to', + 'cert_fingerprint' => 'HEX(c.fingerprint)', + 'cert_dn' => 'GROUP_CONCAT(CONCAT(dn.key, \'=\', dn.value) SEPARATOR \',\')', + 'cert_subject_alt_name' => (new Sql\Select()) + ->from('x509_certificate_subject_alt_name can') + ->columns('GROUP_CONCAT(CONCAT(can.type, \':\', can.value) SEPARATOR \',\')') + ->where(['can.certificate_id = c.id']) + ->groupBy(['can.certificate_id']) + ]) + ->join('x509_certificate_chain cc', 'cc.id = t.latest_certificate_chain_id') + ->join('x509_certificate_chain_link ccl', 'ccl.certificate_chain_id = cc.id') + ->join('x509_certificate c', 'c.id = ccl.certificate_id') + ->joinLeft('x509_certificate ci', 'ci.subject_hash = c.issuer_hash') + ->joinLeft('x509_dn dn', 'dn.hash = c.subject_hash') + ->where(['ccl.order = ?' => 0]) + ->groupBy(['t.ip', 't.hostname', 't.port']); + + $results = []; + foreach ($this->getDb()->select($targets) as $target) { + list($ipv4, $ipv6) = $this->transformIpAddress($target->host_ip); + $target->host_ip = $ipv4 ?: $ipv6; + $target->host_address = $ipv4; + $target->host_address6 = $ipv6; + + $target->host_name_ip_and_port = sprintf( + '%s/%s:%d', + $target->host_name, + $target->host_ip, + $target->host_port + ); + + $results[$target->host_name_ip_and_port] = $target; + } + + return $results; + } + + public function listColumns() + { + return [ + 'host_name_ip_and_port', + 'host_ip', + 'host_name', + 'host_port', + 'host_address', + 'host_address6', + 'cert_subject', + 'cert_issuer', + 'cert_self_signed', + 'cert_trusted', + 'cert_valid_from', + 'cert_valid_to', + 'cert_fingerprint', + 'cert_dn', + 'cert_subject_alt_name' + ]; + } + + public static function getDefaultKeyColumnName() + { + return 'host_name_ip_and_port'; + } +} diff --git a/library/X509/ProvidedHook/x509ImportSource.php b/library/X509/ProvidedHook/x509ImportSource.php new file mode 100644 index 0000000..184744b --- /dev/null +++ b/library/X509/ProvidedHook/x509ImportSource.php @@ -0,0 +1,49 @@ +<?php +// Icinga Web 2 X.509 Module | (c) 2019 Icinga GmbH | GPLv2 + +namespace Icinga\Module\X509\ProvidedHook; + +use Icinga\Application\Config; +use Icinga\Data\ResourceFactory; +use Icinga\Module\Director\Hook\ImportSourceHook; +use ipl\Sql; +use PDO; + +abstract class x509ImportSource extends ImportSourceHook +{ + /** + * Get the connection to the X.509 database + * + * @return Sql\Connection + */ + protected function getDb() + { + $config = new Sql\Config(ResourceFactory::getResourceConfig( + Config::module('x509')->get('backend', 'resource') + )); + $config->options = [ + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ + ]; + + $conn = new Sql\Connection($config); + + return $conn; + } + + /** + * Transform the given binary IP address in a human readable format + * + * @param string $ip + * + * @return array The first element is IPv4, the second IPv6 + */ + protected function transformIpAddress($ip) + { + $ipv4 = ltrim($ip, "\0"); + if (strlen($ipv4) === 4) { + return [inet_ntop($ipv4), null]; + } else { + return [null, inet_ntop($ip)]; + } + } +} |