44
55namespace Icinga \Module \X509 \ProvidedHook ;
66
7- use Icinga \Module \X509 \DbTool ;
7+ use Icinga \Module \X509 \Model \X509CrtSubjAltName ;
8+ use Icinga \Module \X509 \Model \X509Target ;
89use ipl \Sql ;
10+ use ipl \Stdlib \Filter ;
911
1012class ServicesImportSource extends X509ImportSource
1113{
1214 public function fetchData ()
1315 {
14- $ targets = (new Sql \Select ())
15- ->from ('x509_target t ' )
16+ $ targets = X509Target::on ($ this ->getDb ())->with ([
17+ 'chain ' ,
18+ 'chain.certificate ' ,
19+ 'chain.certificate.dn ' ,
20+ 'chain.certificate.issuer ' ,
21+ ]);
22+
23+ $ targets ->getWith ()['x509_target.chain.certificate.issuer ' ]->setJoinTypE ('LEFT ' );
24+ $ targets
1625 ->columns ([
17- 'host_ip ' => 't.ip ' ,
18- 'host_name ' => 't.hostname ' ,
19- 'host_port ' => 't.port ' ,
20- 'cert_subject ' => 'c.subject ' ,
21- 'cert_issuer ' => 'c.issuer ' ,
22- 'cert_self_signed ' => 'COALESCE(ci.self_signed, c.self_signed) ' ,
23- 'cert_trusted ' => 'c.trusted ' ,
24- 'cert_valid_from ' => 'c.valid_from ' ,
25- 'cert_valid_to ' => 'c.valid_to '
26+ 'host_ip ' => 'ip ' ,
27+ 'host_name ' => 'hostname ' ,
28+ 'host_port ' => 'port ' ,
29+ 'cert_subject ' => 'chain.certificate.subject ' ,
30+ 'cert_issuer ' => 'chain.certificate.issuer ' ,
31+ 'cert_trusted ' => 'chain.certificate.trusted ' ,
32+ 'cert_valid_from ' => 'chain.certificate.valid_from ' ,
33+ 'cert_valid_to ' => 'chain.certificate.valid_to ' ,
34+ 'cert_self_signed ' => new Sql \Expression ('COALESCE(%s, %s) ' , [
35+ 'chain.certificate.issuer.self_signed ' ,
36+ 'chain.certificate.self_signed '
37+ ])
2638 ])
27- ->join ('x509_certificate_chain cc ' , 'cc.id = t.latest_certificate_chain_id ' )
28- ->join ('x509_certificate_chain_link ccl ' , 'ccl.certificate_chain_id = cc.id ' )
29- ->join ('x509_certificate c ' , 'c.id = ccl.certificate_id ' )
30- ->joinLeft ('x509_certificate ci ' , 'ci.subject_hash = c.issuer_hash ' )
31- ->joinLeft ('x509_dn dn ' , 'dn.hash = c.subject_hash ' )
32- ->where (['ccl.order = ? ' => 0 ])
33- ->groupBy (['t.ip ' , 't.hostname ' , 't.port ' ]);
34-
35- $ certAltName = (new Sql \Select ())
36- ->from ('x509_certificate_subject_alt_name can ' )
37- ->where (['can.certificate_id = c.id ' ])
38- ->groupBy (['can.certificate_id ' ]);
39-
40- if ($ this ->getDb ()->getConfig ()->db === 'pgsql ' ) {
41- $ targets ->columns ([
42- 'cert_fingerprint ' => 'ENCODE(c.fingerprint, \'hex \') ' ,
43- 'cert_dn ' => 'ARRAY_TO_STRING(ARRAY_AGG(CONCAT(dn.key, \'= \', dn.value)), \', \') '
44- ])
45- ->groupBy (['c.id ' , 'ci.id ' ]);
46-
47- $ certAltName ->columns ('ARRAY_TO_STRING(ARRAY_AGG(CONCAT(can.type, \': \', can.value)), \', \') ' );
39+ ->getSelectBase ()
40+ ->where (new Sql \Expression ('x509_target_chain_x509_certificate_chain_link.order = 0 ' ))
41+ ->groupBy (['ip, hostname, port ' ]);
42+
43+ $ certAltName = X509CrtSubjAltName::on ($ this ->getDb ());
44+ $ certAltName
45+ ->getSelectBase ()
46+ ->where (new Sql \Expression ('certificate_id = x509_target_chain_certificate.id ' ))
47+ ->groupBy (['x509_certificate_subject_alt_name.certificate_id ' ]);
48+
49+ if ($ this ->getDb () instanceof Sql \Adapter \Pgsql) {
50+ $ targets
51+ ->withColumns ([
52+ 'cert_fingerprint ' => new Sql \Expression ('ENCODE(%s, \'hex \') ' , [
53+ 'chain.link.certificate.fingerprint '
54+ ]),
55+ 'cert_dn ' => new Sql \Expression (
56+ 'ARRAY_TO_STRING(ARRAY_AGG(CONCAT(%s, \'= \', %s)), \', \') ' ,
57+ [
58+ 'chain.link.certificate.dn.key ' ,
59+ 'chain.link.certificate.dn.value '
60+ ]
61+ )
62+ ])
63+ ->getSelectBase ()
64+ ->groupBy (['x509_target_chain_link_certificate.id ' , 'x509_target_chain_link_certificate_issuer.id ' ]);
65+
66+ $ certAltName ->columns ([
67+ 'subj_alt_name ' => new Sql \Expression ('ARRAY_TO_STRING(ARRAY_AGG(CONCAT(type, \': \', value)), \', \') ' )
68+ ]);
4869 } else {
49- $ targets ->columns ([
50- 'cert_fingerprint ' => 'HEX(c.fingerprint) ' ,
51- 'cert_dn ' => 'GROUP_CONCAT(CONCAT(dn.key, \'= \', dn.value) SEPARATOR \', \') '
70+ $ targets ->withColumns ([
71+ 'cert_fingerprint ' => new Sql \Expression ('HEX(%s) ' , ['chain.certificate.fingerprint ' ]),
72+ 'cert_dn ' => new Sql \Expression (
73+ 'GROUP_CONCAT(CONCAT(%s, \'= \', %s) SEPARATOR \', \') ' ,
74+ [
75+ 'chain.certificate.dn.key ' ,
76+ 'chain.certificate.dn.value '
77+ ]
78+ )
5279 ]);
5380
54- $ certAltName ->columns ('GROUP_CONCAT(CONCAT(can. type, \': \', can. value) SEPARATOR \', \') ' );
81+ $ certAltName ->columns ([ new Sql \ Expression ( 'GROUP_CONCAT(CONCAT(type, \': \', value) SEPARATOR \', \') ' )] );
5582 }
5683
57- $ targets ->columns (['cert_subject_alt_name ' => $ certAltName ]);
84+ list ($ select , $ values ) = $ certAltName ->dump ();
85+ $ targets ->withColumns (['cert_subject_alt_name ' => new Sql \Expression ("$ select " , null , ...$ values )]);
5886
5987 $ results = [];
60- foreach ($ this ->getDb ()->select ($ targets ) as $ target ) {
61- if ($ this ->getDb ()->getConfig ()->db === 'pgsql ' ) {
62- $ target ->host_ip = DbTool::unmarshalBinary ($ target ->host_ip );
63- }
64-
88+ foreach ($ targets as $ target ) {
6589 list ($ ipv4 , $ ipv6 ) = $ this ->transformIpAddress ($ target ->host_ip );
6690 $ target ->host_ip = $ ipv4 ?: $ ipv6 ;
6791 $ target ->host_address = $ ipv4 ;
@@ -74,7 +98,10 @@ public function fetchData()
7498 $ target ->host_port
7599 );
76100
77- $ results [$ target ->host_name_ip_and_port ] = $ target ;
101+ $ properties = iterator_to_array ($ target ->getIterator ());
102+ unset($ properties ['chain ' ]); // We don't need any relation properties anymore
103+
104+ $ results [$ target ->host_name_ip_and_port ] = (object ) $ properties ;
78105 }
79106
80107 return $ results ;
0 commit comments