Skip to content

Commit 3d2adfd

Browse files
raviks789yhabteab
authored andcommitted
Manage targets and certificates based on their availability
1 parent be67bc8 commit 3d2adfd

File tree

2 files changed

+119
-0
lines changed

2 files changed

+119
-0
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
/* Icinga Web 2 X.509 Module | (c) 2023 Icinga GmbH | GPLv2 */
4+
5+
namespace Icinga\Module\X509\Clicommands;
6+
7+
use DateTime;
8+
use Exception;
9+
use Icinga\Application\Logger;
10+
use Icinga\Module\X509\CertificateUtils;
11+
use Icinga\Module\X509\Command;
12+
use InvalidArgumentException;
13+
use Throwable;
14+
15+
class CleanupCommand extends Command
16+
{
17+
/**
18+
* Clean up certificates that aren't used by any host and targets whose last scan is older than a certain date/time
19+
*
20+
* This command purges by default all targets whose last scan is older than 1 month. The last_scan column is
21+
* always updated when scanning a target, regardless of whether a successful connection to the host has been
22+
* established or not. Additionally, it removes all untrusted certificates that aren't part of any chains.
23+
*
24+
* USAGE
25+
*
26+
* icingacli x509 cleanup [OPTIONS]
27+
*
28+
* OPTIONS
29+
*
30+
* --since-last-scan=<datetime>
31+
* Clean up targets whose last scan is older than the specified date/time,
32+
* which can also be an English textual datetime description like "2 days".
33+
* Default "1 month".
34+
*
35+
* EXAMPLES
36+
*
37+
* Remove all targets that haven't been scanned for 2 months and all unused untrusted certificates:
38+
*
39+
* icingacli x509 cleanup --since-last-scan="2 months"
40+
*
41+
*/
42+
public function indexAction()
43+
{
44+
$sinceLastScan = $this->params->get('since-last-scan', '-1 month');
45+
$lastScan = $sinceLastScan;
46+
if ($lastScan[0] !== '-') {
47+
// When the user specified "2 days" as a threshold strtotime() will compute the
48+
// timestamp NOW() + 2 days, but it has to be NOW() + (-2 days)
49+
$lastScan = "-$lastScan";
50+
}
51+
52+
try {
53+
$sinceLastScan = new DateTime($lastScan);
54+
} catch (Exception $_) {
55+
throw new InvalidArgumentException(sprintf(
56+
'The specified last scan time is in an unknown format: %s',
57+
$sinceLastScan
58+
));
59+
}
60+
61+
try {
62+
$conn = $this->getDb();
63+
$query = $conn->delete(
64+
'x509_target',
65+
['last_scan < ?' => $sinceLastScan->format('Uv')]
66+
);
67+
68+
if ($query->rowCount() > 0) {
69+
Logger::info(
70+
'Removed %d targets matching since last scan filter: %s',
71+
$query->rowCount(),
72+
$sinceLastScan->format(DateTime::ATOM)
73+
);
74+
75+
CertificateUtils::cleanupCertificateChains($conn);
76+
CertificateUtils::cleanupCertificates($conn);
77+
}
78+
} catch (Throwable $err) {
79+
Logger::error($err);
80+
}
81+
}
82+
}

library/X509/CertificateUtils.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
use Icinga\Module\X509\Model\X509CertificateChain;
1212
use Icinga\Module\X509\Model\X509CertificateSubjectAltName;
1313
use Icinga\Module\X509\Model\X509Dn;
14+
use Icinga\Module\X509\Model\X509Target;
1415
use ipl\Sql\Connection;
1516
use ipl\Sql\Expression;
17+
use ipl\Sql\Select;
1618
use ipl\Stdlib\Filter;
1719

1820
class CertificateUtils
@@ -365,6 +367,41 @@ private static function findOrInsertDn($db, $certInfo, $type)
365367
return $hash;
366368
}
367369

370+
/**
371+
* Cleanup certificates that aren't currently part of any chains
372+
*
373+
* @param Connection $db
374+
*/
375+
public static function cleanupCertificates(Connection $db)
376+
{
377+
$certsQuery = $db->delete('x509_certificate', [
378+
'id NOT IN ?' => (new Select())
379+
->from('x509_certificate_chain_link ccl')
380+
->columns(['ccl.certificate_id'])
381+
->distinct(),
382+
'trusted = ?' => 'n',
383+
]);
384+
385+
$rows = $certsQuery->rowCount();
386+
if ($rows > 0) {
387+
Logger::info('Removed %d certificates that are not part of any chains', $rows);
388+
}
389+
}
390+
391+
/**
392+
* Cleanup certificate chains that aren't used by any targets
393+
*
394+
* @param Connection $db
395+
*/
396+
public static function cleanupCertificateChains(Connection $db)
397+
{
398+
list($chainIdSelect, $_) = X509Target::on($db)
399+
->columns('latest_certificate_chain_id')
400+
->dump();
401+
402+
$db->delete('x509_certificate_chain', ['id NOT IN (?)' => $chainIdSelect]);
403+
}
404+
368405
/**
369406
* Verify certificates
370407
*

0 commit comments

Comments
 (0)