Rev 4 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 4 | Rev 52 | ||
---|---|---|---|
1 | <?php /* <ViaThinkSoftSignature> |
1 | <?php /* <ViaThinkSoftSignature> |
2 | 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 |
23 | w== |
23 | A== |
24 | </ViaThinkSoftSignature> */ ?> |
24 | </ViaThinkSoftSignature> */ ?> |
25 | <?php |
25 | <?php |
26 | 26 | ||
27 | /* |
27 | /* |
28 | * VNag - Nagios Framework for PHP |
28 | * VNag - Nagios Framework for PHP |
29 | * Developed by Daniel Marschall, ViaThinkSoft <www.viathinksoft.com> |
29 | * Developed by Daniel Marschall, ViaThinkSoft <www.viathinksoft.com> |
30 | * Licensed under the terms of the Apache 2.0 license |
30 | * Licensed under the terms of the Apache 2.0 license |
31 | * |
31 | * |
32 | * Revision 2018-07-16 |
32 | * Revision 2018-07-16 |
33 | */ |
33 | */ |
34 | 34 | ||
35 | declare(ticks=1); |
35 | declare(ticks=1); |
36 | 36 | ||
37 | class X509ExpireCheck extends VNag { |
37 | class X509ExpireCheck extends VNag { |
38 | protected $argFiles = null; |
38 | protected $argFiles = null; |
39 | 39 | ||
40 | public function __construct() { |
40 | public function __construct() { |
41 | parent::__construct(); |
41 | parent::__construct(); |
42 | 42 | ||
43 | $this->registerExpectedStandardArguments('Vhtwcv'); |
43 | $this->registerExpectedStandardArguments('Vhtwcv'); |
44 | 44 | ||
45 | $this->getHelpManager()->setPluginName('check_x509_expire'); |
45 | $this->getHelpManager()->setPluginName('check_x509_expire'); |
46 | $this->getHelpManager()->setVersion('1.0'); |
46 | $this->getHelpManager()->setVersion('1.0'); |
47 | $this->getHelpManager()->setShortDescription('This plugin checks X.509 (PEM) files and warns if certificates are about to expire.'); |
47 | $this->getHelpManager()->setShortDescription('This plugin checks X.509 (PEM) files and warns if certificates are about to expire.'); |
48 | $this->getHelpManager()->setCopyright('Copyright (C) 2011-$CURYEAR$ Daniel Marschall, ViaThinkSoft.'); |
48 | $this->getHelpManager()->setCopyright('Copyright (C) 2011-$CURYEAR$ Daniel Marschall, ViaThinkSoft.'); |
49 | $this->getHelpManager()->setSyntax('$SCRIPTNAME$ [-v] -w <warnSeconds>s -c <critSeconds>s -f "[#]<mask>" [-f "[#]<mask>" [...]]'); |
49 | $this->getHelpManager()->setSyntax('$SCRIPTNAME$ [-v] -w <warnSeconds>s -c <critSeconds>s -f "[#]<mask>" [-f "[#]<mask>" [...]]'); |
50 | $this->getHelpManager()->setFootNotes('If you encounter bugs, please contact ViaThinkSoft at www.viathinksoft.com'); |
50 | $this->getHelpManager()->setFootNotes('If you encounter bugs, please contact ViaThinkSoft at www.viathinksoft.com'); |
51 | 51 | ||
52 | // Individual (non-standard) arguments: |
52 | // Individual (non-standard) arguments: |
53 | $this->addExpectedArgument($this->argFiles = new VNagArgument('f', 'file', VNagArgument::VALUE_REQUIRED, 'mask', 'The files to be checked. This argument can be used multiple times. Wilcards may be used but MUST be passed as string only (not resolved by the Shell). There are two possible checking modes: If you put a # in front of the file mask, only the oldest file of each group will be checked (use this mode e.g. if you have a directory which contains old backups of certificates beside the current working certificate). Otherwise, all files of the file group are checked.')); |
53 | $this->addExpectedArgument($this->argFiles = new VNagArgument('f', 'file', VNagArgument::VALUE_REQUIRED, 'mask', 'The files to be checked. This argument can be used multiple times. Wilcards may be used but MUST be passed as string only (not resolved by the Shell). There are two possible checking modes: If you put a # in front of the file mask, only the oldest file of each group will be checked (use this mode e.g. if you have a directory which contains old backups of certificates beside the current working certificate). Otherwise, all files of the file group are checked.')); |
54 | 54 | ||
55 | // In this context, when the user writes "-w 60s" then he actually means "-w @~:60s" or "-w 60s:~", so these commands allow this notation: |
55 | // In this context, when the user writes "-w 60s" then they probably mean "-w @60s". Make sure that the user doesn't do it wrong |
56 | $this->warningSingleValueRangeBehaviors[0] = self::SINGLEVALUE_RANGE_VAL_LT_X_BAD; |
56 | $this->warningSingleValueRangeBehaviors[0] = self::SINGLEVALUE_RANGE_VAL_LT_X_BAD; |
57 | $this->criticalSingleValueRangeBehaviors[0] = self::SINGLEVALUE_RANGE_VAL_LT_X_BAD; |
57 | $this->criticalSingleValueRangeBehaviors[0] = self::SINGLEVALUE_RANGE_VAL_LT_X_BAD; |
58 | } |
58 | } |
59 | 59 | ||
60 | private static function humanFriendlyTimeLeft($secs) { |
60 | private static function humanFriendlyTimeLeft($secs) { |
61 | $out = array(); |
61 | $out = array(); |
62 | 62 | ||
63 | if ($expired = $secs < 0) $secs *= -1; |
63 | if ($expired = $secs < 0) $secs *= -1; |
64 | 64 | ||
65 | $years = floor($secs / 60 / 60 / 24 / 365); |
65 | $years = floor($secs / 60 / 60 / 24 / 365); |
66 | if ($years > 0) $out[] = $years == 1 ? "$years year" : "$years years"; |
66 | if ($years > 0) $out[] = $years == 1 ? "$years year" : "$years years"; |
67 | 67 | ||
68 | $days = floor($secs / 60 / 60 / 24) % 365; |
68 | $days = floor($secs / 60 / 60 / 24) % 365; |
69 | if ($days > 0) $out[] = $days == 1 ? "$days day" : "$days days"; |
69 | if ($days > 0) $out[] = $days == 1 ? "$days day" : "$days days"; |
70 | 70 | ||
71 | $hours = floor($secs / 60 / 60) % 24; |
71 | $hours = floor($secs / 60 / 60) % 24; |
72 | if ($hours > 0) $out[] = $hours == 1 ? "$hours hour" : "$hours hours"; |
72 | if ($hours > 0) $out[] = $hours == 1 ? "$hours hour" : "$hours hours"; |
73 | 73 | ||
74 | $minutes = floor($secs / 60) % 60; |
74 | $minutes = floor($secs / 60) % 60; |
75 | if ($minutes > 0) $out[] = $minutes == 1 ? "$minutes minute" : "$minutes minutes"; |
75 | if ($minutes > 0) $out[] = $minutes == 1 ? "$minutes minute" : "$minutes minutes"; |
76 | 76 | ||
77 | $seconds = $secs % 60; |
77 | $seconds = $secs % 60; |
78 | if ($seconds > 0) $out[] = $seconds == 1 ? "$seconds second" : "$seconds seconds"; |
78 | if ($seconds > 0) $out[] = $seconds == 1 ? "$seconds second" : "$seconds seconds"; |
79 | 79 | ||
80 | return ($expired ? 'EXPIRED SINCE ' : '').implode(", ", $out).($expired ? '' : ' left'); |
80 | return ($expired ? 'EXPIRED SINCE ' : '').implode(", ", $out).($expired ? '' : ' left'); |
81 | } |
81 | } |
82 | 82 | ||
83 | private static function timeLeft($pemFile) { |
83 | private static function timeLeft($pemFile) { |
84 | $out = array(); |
84 | $out = array(); |
85 | 85 | ||
86 | // TODO: Call PHP's openssl functions instead |
86 | // TODO: Call PHP's openssl functions instead |
87 | exec("openssl x509 -in ".escapeshellarg($pemFile)." -noout -text | grep \"Not After\" | cut -d ':' -f 2-", $out, $code); // TODO: check $code |
87 | exec("openssl x509 -in ".escapeshellarg($pemFile)." -noout -text | grep \"Not After\" | cut -d ':' -f 2-", $out, $code); // TODO: check $code |
88 | if ($code != 0) { |
88 | if ($code != 0) { |
89 | throw new VNagException("Error calling openssl!"); |
89 | throw new VNagException("Error calling openssl!"); |
90 | } |
90 | } |
91 | 91 | ||
92 | $tim = strtotime($out[0]); |
92 | $tim = strtotime($out[0]); |
93 | return $tim - time(); |
93 | return $tim - time(); |
94 | } |
94 | } |
95 | 95 | ||
96 | protected function cbRun($optional_args=array()) { |
96 | protected function cbRun($optional_args=array()) { |
97 | $this->argFiles->require(); |
97 | $this->argFiles->require(); |
98 | 98 | ||
99 | $countFilesTotal = 0; |
99 | $countFilesTotal = 0; |
100 | $countFilesCrit = 0; |
100 | $countFilesCrit = 0; |
101 | $countFilesWarn = 0; |
101 | $countFilesWarn = 0; |
102 | 102 | ||
103 | $fileGroupMasks = $this->argFiles->getValue(); |
103 | $fileGroupMasks = $this->argFiles->getValue(); |
104 | if (!is_array($fileGroupMasks)) $fileGroupMasks = array($fileGroupMasks); |
104 | if (!is_array($fileGroupMasks)) $fileGroupMasks = array($fileGroupMasks); |
105 | foreach ($fileGroupMasks as $fileGroupMask) { |
105 | foreach ($fileGroupMasks as $fileGroupMask) { |
106 | if (substr($fileGroupMask, 0, 1) === '#') { |
106 | if (substr($fileGroupMask, 0, 1) === '#') { |
107 | $fileGroupMask = substr($fileGroupMask, 1); // remove # |
107 | $fileGroupMask = substr($fileGroupMask, 1); // remove # |
108 | 108 | ||
109 | // Mode 1: Only the youngest file of each group is checked. |
109 | // Mode 1: Only the youngest file of each group is checked. |
110 | // You can use this mode e.g. if you have a folder with downloaded files |
110 | // You can use this mode e.g. if you have a folder with downloaded files |
111 | // and you want to check if a downloading-script is still downloading |
111 | // and you want to check if a downloading-script is still downloading |
112 | // new files regularly. |
112 | // new files regularly. |
113 | 113 | ||
114 | $files = glob($fileGroupMask); |
114 | $files = glob($fileGroupMask); |
115 | if (count($files) == 0) continue; |
115 | if (count($files) == 0) continue; |
116 | 116 | ||
117 | $minTimeLeft = null; |
117 | $minTimeLeft = null; |
118 | foreach ($files as $file) { |
118 | foreach ($files as $file) { |
119 | $minTimeLeft = is_null($minTimeLeft) ? filemtime($file) : min($minTimeLeft, self::timeLeft($file)); |
119 | $minTimeLeft = is_null($minTimeLeft) ? filemtime($file) : min($minTimeLeft, self::timeLeft($file)); |
120 | } |
120 | } |
121 | 121 | ||
122 | $countFilesTotal++; |
122 | $countFilesTotal++; |
123 | if ($this->checkAgainstCriticalRange($minTimeLeft.'s', false, true)) { |
123 | if ($this->checkAgainstCriticalRange($minTimeLeft.'s', false, true)) { |
124 | $countFilesCrit++; |
124 | $countFilesCrit++; |
125 | $this->addVerboseMessage("File group '$fileGroupMask' oldest file: ".self::humanFriendlyTimeLeft($minTimeLeft)." (Critical)\n", VNag::VERBOSITY_SUMMARY); |
125 | $this->addVerboseMessage("File group '$fileGroupMask' oldest file: ".self::humanFriendlyTimeLeft($minTimeLeft)." (Critical)\n", VNag::VERBOSITY_SUMMARY); |
126 | } else if ($this->checkAgainstWarningRange($minTimeLeft.'s', false, true)) { |
126 | } else if ($this->checkAgainstWarningRange($minTimeLeft.'s', false, true)) { |
127 | $countFilesWarn++; |
127 | $countFilesWarn++; |
128 | $this->addVerboseMessage("File group '$fileGroupMask' oldest file: ".self::humanFriendlyTimeLeft($minTimeLeft)." (Warning)\n", VNag::VERBOSITY_SUMMARY); |
128 | $this->addVerboseMessage("File group '$fileGroupMask' oldest file: ".self::humanFriendlyTimeLeft($minTimeLeft)." (Warning)\n", VNag::VERBOSITY_SUMMARY); |
129 | } else { |
129 | } else { |
130 | if (($this->getArgumentHandler()->getArgumentObj('w')->available()) || ($this->getArgumentHandler()->getArgumentObj('c')->available())) { |
130 | if (($this->getArgumentHandler()->getArgumentObj('w')->available()) || ($this->getArgumentHandler()->getArgumentObj('c')->available())) { |
131 | $this->addVerboseMessage("File group '$fileGroupMask' oldest file: ".self::humanFriendlyTimeLeft($minTimeLeft)." (OK)\n", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
131 | $this->addVerboseMessage("File group '$fileGroupMask' oldest file: ".self::humanFriendlyTimeLeft($minTimeLeft)." (OK)\n", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
132 | } else { |
132 | } else { |
133 | $this->addVerboseMessage("File group '$fileGroupMask' oldest file: ".self::humanFriendlyTimeLeft($minTimeLeft)."\n", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
133 | $this->addVerboseMessage("File group '$fileGroupMask' oldest file: ".self::humanFriendlyTimeLeft($minTimeLeft)."\n", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
134 | } |
134 | } |
135 | } |
135 | } |
136 | } else { |
136 | } else { |
137 | // Mode 2: All files of each group are checked. |
137 | // Mode 2: All files of each group are checked. |
138 | 138 | ||
139 | $files = glob($fileGroupMask); |
139 | $files = glob($fileGroupMask); |
140 | if (count($files) == 0) continue; |
140 | if (count($files) == 0) continue; |
141 | 141 | ||
142 | foreach ($files as $file) { |
142 | foreach ($files as $file) { |
143 | $timeLeft = self::timeLeft($file); |
143 | $timeLeft = self::timeLeft($file); |
144 | $countFilesTotal++; |
144 | $countFilesTotal++; |
145 | if ($this->checkAgainstCriticalRange($timeLeft.'s', false, true)) { |
145 | if ($this->checkAgainstCriticalRange($timeLeft.'s', false, true)) { |
146 | $countFilesCrit++; |
146 | $countFilesCrit++; |
147 | $this->addVerboseMessage("File $file: ".self::humanFriendlyTimeLeft($timeLeft)." (Critical)\n", VNag::VERBOSITY_SUMMARY); |
147 | $this->addVerboseMessage("File $file: ".self::humanFriendlyTimeLeft($timeLeft)." (Critical)\n", VNag::VERBOSITY_SUMMARY); |
148 | } else if ($this->checkAgainstWarningRange($timeLeft.'s', false, true)) { |
148 | } else if ($this->checkAgainstWarningRange($timeLeft.'s', false, true)) { |
149 | $countFilesWarn++; |
149 | $countFilesWarn++; |
150 | $this->addVerboseMessage("File $file: ".self::humanFriendlyTimeLeft($timeLeft)." (Warning)\n", VNag::VERBOSITY_SUMMARY); |
150 | $this->addVerboseMessage("File $file: ".self::humanFriendlyTimeLeft($timeLeft)." (Warning)\n", VNag::VERBOSITY_SUMMARY); |
151 | } else { |
151 | } else { |
152 | if (($this->getArgumentHandler()->getArgumentObj('w')->available()) || ($this->getArgumentHandler()->getArgumentObj('c')->available())) { |
152 | if (($this->getArgumentHandler()->getArgumentObj('w')->available()) || ($this->getArgumentHandler()->getArgumentObj('c')->available())) { |
153 | $this->addVerboseMessage("File $file: ".self::humanFriendlyTimeLeft($timeLeft)." (OK)\n", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
153 | $this->addVerboseMessage("File $file: ".self::humanFriendlyTimeLeft($timeLeft)." (OK)\n", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
154 | } else { |
154 | } else { |
155 | $this->addVerboseMessage("File $file: ".self::humanFriendlyTimeLeft($timeLeft)."\n", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
155 | $this->addVerboseMessage("File $file: ".self::humanFriendlyTimeLeft($timeLeft)."\n", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
156 | } |
156 | } |
157 | } |
157 | } |
158 | } |
158 | } |
159 | } |
159 | } |
160 | } |
160 | } |
161 | 161 | ||
162 | $msg = array(); |
162 | $msg = array(); |
163 | $msg[] = "Checked $countFilesTotal certificates"; |
163 | $msg[] = "Checked $countFilesTotal certificates"; |
164 | if ($this->getArgumentHandler()->getArgumentObj('w')->available()) $msg[] = "$countFilesWarn are in warning time range"; |
164 | if ($this->getArgumentHandler()->getArgumentObj('w')->available()) $msg[] = "$countFilesWarn are in warning time range"; |
165 | if ($this->getArgumentHandler()->getArgumentObj('c')->available()) $msg[] = "$countFilesCrit are in critical time range"; |
165 | if ($this->getArgumentHandler()->getArgumentObj('c')->available()) $msg[] = "$countFilesCrit are in critical time range"; |
166 | $msg = implode(", ", $msg); |
166 | $msg = implode(", ", $msg); |
167 | 167 | ||
168 | $this->setHeadLine($msg); |
168 | $this->setHeadLine($msg); |
169 | } |
169 | } |
170 | } |
170 | } |
171 | 171 |