summaryrefslogtreecommitdiffstats
path: root/library/X509/ProvidedHook
diff options
context:
space:
mode:
Diffstat (limited to 'library/X509/ProvidedHook')
-rw-r--r--library/X509/ProvidedHook/HostsImportSource.php70
-rw-r--r--library/X509/ProvidedHook/ServicesImportSource.php85
-rw-r--r--library/X509/ProvidedHook/x509ImportSource.php49
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)];
+ }
+ }
+}