|
1 | 1 | import requests
|
2 |
| -import logging |
| 2 | +import alog |
3 | 3 | from kubernetes import client, config
|
4 | 4 | from prometheus_client import Gauge
|
5 | 5 | import urllib3
|
|
8 | 8 | import ssl, socket
|
9 | 9 | import datetime
|
10 | 10 | import time
|
| 11 | +import sys |
11 | 12 |
|
12 | 13 | urllib3.disable_warnings()
|
13 |
| -logger = logging.getLogger(__name__) |
14 |
| - |
15 |
| - |
16 | 14 |
|
17 | 15 | class CertsNet(object):
|
18 | 16 | namespace = None
|
19 | 17 | use_kubeconfig = True
|
20 | 18 | trawler = None
|
| 19 | + logger = None |
21 | 20 |
|
22 |
| - def __init__(self, config, trawler): |
| 21 | + def __init__(self, config, trawler=None, logger=None): |
23 | 22 | # Takes in config object and trawler instance it's behind
|
24 | 23 | # Use kubeconfig or in-cluster config for k8s comms
|
25 | 24 | if trawler:
|
26 | 25 | self.trawler = trawler
|
27 |
| - self.use_kubeconfig = trawler.use_kubeconfig |
| 26 | + self.use_kubeconfig = self.trawler.use_kubeconfig |
| 27 | + if logger: |
| 28 | + self.logger = logger |
| 29 | + else: |
| 30 | + alog.configure(default_level="info") |
| 31 | + self.logger = alog.use_channel("certs_net") |
| 32 | + self.logger.info("Init CertsNet") |
28 | 33 | # Namespace to review
|
29 | 34 | self.namespace = config.get('namespace', None)
|
30 | 35 |
|
| 36 | + |
31 | 37 | def getExpiry(self, cert_data):
|
32 |
| - cert = base64.b64decode(cert_data) |
33 |
| - x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) |
34 |
| - expiry = datetime.datetime.strptime(x509.get_notAfter().decode('utf-8'), "%Y%m%d%H%M%S%z").timestamp() |
35 |
| - return int(expiry - time.time()) |
| 38 | + try: |
| 39 | + cert = base64.b64decode(cert_data) |
| 40 | + x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) |
| 41 | + expiry = datetime.datetime.strptime(x509.get_notAfter().decode('utf-8'), "%Y%m%d%H%M%S%z").timestamp() |
| 42 | + return int(expiry - time.time()) |
| 43 | + except: |
| 44 | + self.logger.exception("getExpiry failed") |
| 45 | + return None |
36 | 46 |
|
| 47 | + @alog.timed_function(alog.use_channel("fish").trace) |
37 | 48 | def fish(self):
|
38 | 49 | # Go fishing
|
39 |
| - # Load appropriate k8s config |
40 |
| - if self.use_kubeconfig: |
41 |
| - config.load_kube_config() |
42 |
| - else: |
43 |
| - config.load_incluster_config() |
44 |
| - # Initialise the k8s API |
45 |
| - v1 = client.CoreV1Api() |
46 |
| - # Retreive secret list for specified namespace if specified, otherwise all namespaces |
47 |
| - if self.namespace: |
48 |
| - ret = v1.list_namespaced_secret(namespace=self.namespace) |
49 |
| - else: |
50 |
| - ret = v1.list_secret_for_all_namespaces() |
| 50 | + if self.logger: |
| 51 | + self.logger.info("Starting to fish") |
| 52 | + try: |
| 53 | + # Load appropriate k8s config |
| 54 | + if self.use_kubeconfig: |
| 55 | + config.load_kube_config() |
| 56 | + else: |
| 57 | + config.load_incluster_config() |
| 58 | + except: |
| 59 | + self.logger.exception("fish: loading kubeconfig failed") |
| 60 | + |
| 61 | + try: |
| 62 | + # Initialise the k8s API |
| 63 | + v1 = client.CoreV1Api() |
| 64 | + except: |
| 65 | + self.logger.exception("fish: loading kubenetes CoreV1Api failed") |
| 66 | + return |
51 | 67 |
|
| 68 | + try: |
| 69 | + # Retreive secret list for specified namespace if specified, otherwise all namespaces |
| 70 | + if self.namespace: |
| 71 | + self.logger.info("CertsNet: Getting secrets for namespace {}".format(self.namespace)) |
| 72 | + ret = v1.list_namespaced_secret(namespace=self.namespace) |
| 73 | + else: |
| 74 | + self.logger.info("CertsNet: Getting secrets for all namespaces") |
| 75 | + ret = v1.list_secret_for_all_namespaces() |
| 76 | + except: |
| 77 | + self.logger.exception("fish: retrieving kubenetes secrets failed") |
| 78 | + return |
| 79 | + |
| 80 | + tls_secrets_found = 0 |
| 81 | + secrets_processed = 0 |
52 | 82 | for secret in ret.items:
|
53 |
| - if secret.type == 'kubernetes.io/tls' and 'ca.crt' in secret.data and secret.data['ca.crt'] != '': |
54 |
| - caSecondsLeft = self.getExpiry(secret.data['ca.crt']) |
55 |
| - tlsSecondsLeft = self.getExpiry(secret.data['tls.crt']) |
56 |
| - self.trawler.set_gauge( |
57 |
| - 'cert', 'remaining_seconds', |
58 |
| - tlsSecondsLeft, |
59 |
| - labels={'secret':secret.metadata.name, 'cert':'tls.crt', 'namespace': secret.metadata.namespace}) |
60 |
| - self.trawler.set_gauge( |
61 |
| - 'cert', 'remaining_seconds', |
62 |
| - caSecondsLeft, |
63 |
| - labels={'secret':secret.metadata.name, 'cert':'ca.crt', 'namespace': secret.metadata.namespace}) |
| 83 | + if secret.type == 'kubernetes.io/tls': |
| 84 | + tls_secrets_found += 1 |
| 85 | + this_secret_counted = False |
| 86 | + if 'tls.crt' in secret.data and secret.data['tls.crt'] != '': |
| 87 | + self.logger.trace("Processing tls.crt for secret {}".format(secret.metadata.name)) |
| 88 | + tlsSecondsLeft = self.getExpiry(secret.data['tls.crt']) |
| 89 | + if tlsSecondsLeft == None: |
| 90 | + continue |
| 91 | + if self.trawler: |
| 92 | + self.trawler.set_gauge( |
| 93 | + 'cert', 'remaining_seconds', |
| 94 | + tlsSecondsLeft, |
| 95 | + labels={'secret':secret.metadata.name, 'cert':'tls.crt', 'namespace': secret.metadata.namespace}) |
| 96 | + this_secret_counted = True |
| 97 | + secrets_processed += 1 |
| 98 | + if 'ca.crt' in secret.data and secret.data['ca.crt'] != '': |
| 99 | + self.logger.trace("Processing ca.crt for secret {}".format(secret.metadata.name)) |
| 100 | + caSecondsLeft = self.getExpiry(secret.data['ca.crt']) |
| 101 | + if caSecondsLeft == None: |
| 102 | + continue |
| 103 | + if self.trawler: |
| 104 | + self.trawler.set_gauge( |
| 105 | + 'cert', 'remaining_seconds', |
| 106 | + caSecondsLeft, |
| 107 | + labels={'secret':secret.metadata.name, 'cert':'ca.crt', 'namespace': secret.metadata.namespace}) |
| 108 | + if not this_secret_counted: |
| 109 | + secrets_processed += 1 |
| 110 | + else: |
| 111 | + self.logger.trace("No ca.crt for secret {}".format(secret.metadata.name)) |
| 112 | + if self.logger: |
| 113 | + self.logger.info("Finished fish. Processed {}/{}".format(secrets_processed, tls_secrets_found)) |
| 114 | + |
64 | 115 |
|
65 |
| - |
66 | 116 | if __name__ == "__main__":
|
67 |
| - net = CertsNet(config={'namespace':'apic'}, trawler=None) |
| 117 | + test_logger = None |
| 118 | + if len(sys.argv) > 1: |
| 119 | + alog.configure(default_level="trace") |
| 120 | + test_logger = alog.use_channel("mocktrawler") |
| 121 | + net = CertsNet(config={'namespace':'apic'}, trawler=None, logger=test_logger) |
68 | 122 | net.fish()
|
0 commit comments