summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--roles/openshift_certificate_expiry/README.md312
-rw-r--r--roles/openshift_certificate_expiry/examples/cert-expiry-report-html.pngbin0 -> 189466 bytes
-rw-r--r--roles/openshift_certificate_expiry/examples/cert-expiry-report.html396
-rw-r--r--roles/openshift_certificate_expiry/examples/cert-expiry-report.json178
-rw-r--r--roles/openshift_certificate_expiry/examples/playbooks/default.yaml10
-rw-r--r--roles/openshift_certificate_expiry/examples/playbooks/easy-mode.yaml21
-rw-r--r--roles/openshift_certificate_expiry/examples/playbooks/html_and_json_default_paths.yaml12
-rw-r--r--roles/openshift_certificate_expiry/examples/playbooks/longer-warning-period-json-results.yaml13
-rw-r--r--roles/openshift_certificate_expiry/examples/playbooks/longer_warning_period.yaml12
-rw-r--r--roles/openshift_certificate_expiry/library/openshift_cert_expiry.py35
-rw-r--r--roles/openshift_certificate_expiry/templates/cert-expiry-table.html.j229
11 files changed, 911 insertions, 107 deletions
diff --git a/roles/openshift_certificate_expiry/README.md b/roles/openshift_certificate_expiry/README.md
index a88470bdd..327cc004b 100644
--- a/roles/openshift_certificate_expiry/README.md
+++ b/roles/openshift_certificate_expiry/README.md
@@ -1,5 +1,4 @@
-OpenShift Certificate Expiration Checker
-========================================
+# OpenShift Certificate Expiration Checker
OpenShift certificate expiration checking. Be warned of certificates
expiring within a configurable window of days, and notified of
@@ -21,8 +20,7 @@ cluster. For best results run `ansible-playbook` with the `-v` option.
-Role Variables
---------------
+# Role Variables
Core variables in this role:
@@ -42,8 +40,64 @@ Optional report/result saving variables in this role:
| `openshift_certificate_expiry_json_results_path` | `/tmp/cert-expiry-report.json` | The full path to save the json report as |
-Example Playbook
-----------------
+# Using this Role
+
+How to use the Certificate Expiration Checking Role.
+
+> **NOTE:** In the examples shown below, ensure you change **HOSTS**
+> to the path of your inventory file.
+
+## Run with ansible-playbook
+
+Run one of the example playbooks using an inventory file
+representative of your existing cluster. Some example playbooks are
+included in this repo, or you can read on below after this example to
+craft you own.
+
+```
+$ ansible-playbook -v -i HOSTS ./roles/openshift_certificate_expiry/examples/playbooks/easy-mode.yaml
+```
+
+Using the `easy-mode.yaml` playbook will produce:
+
+* Reports including healthy and unhealthy hosts
+* A JSON report in `/tmp/`
+* A stylized HTML report in `/tmp/`
+
+
+## More Example Playbooks
+
+> **Note:** These Playbooks are available to run directly out of the
+> [examples/playbooks/](examples/playbooks/) directory.
+
+
+This example playbook is great if you're just wanting to **try the
+role out**. This playbook enables HTML and JSON reports. The warning
+window is set very large so you will almost always get results back.
+All certificates (healthy or not) are included in the results:
+
+```yaml
+---
+- name: Check cert expirys
+ hosts: nodes:masters:etcd
+ become: yes
+ gather_facts: no
+ vars:
+ openshift_certificate_expiry_warning_days: 1500
+ openshift_certificate_expiry_save_json_results: yes
+ openshift_certificate_expiry_generate_html_report: yes
+ openshift_certificate_expiry_show_all: yes
+ roles:
+ - role: openshift_certificate_expiry
+```
+
+```
+$ ansible-playbook -v -i HOSTS ./roles/openshift_certificate_expiry/examples/playbooks/easy-mode.yaml
+```
+
+> [View This Playbook](examples/playbooks/easy-mode.yaml)
+
+***
Default behavior:
@@ -57,6 +111,16 @@ Default behavior:
- role: openshift_certificate_expiry
```
+```
+$ ansible-playbook -v -i HOSTS ./roles/openshift_certificate_expiry/examples/playbooks/default.yaml
+```
+
+
+> [View This Playbook](examples/playbooks/default.yaml)
+
+***
+
+
Generate HTML and JSON artifacts in their default paths:
```yaml
@@ -72,6 +136,15 @@ Generate HTML and JSON artifacts in their default paths:
- role: openshift_certificate_expiry
```
+```
+$ ansible-playbook -v -i HOSTS ./roles/openshift_certificate_expiry/examples/playbooks/html_and_json_default_paths.yaml
+```
+
+
+> [View This Playbook](examples/playbooks/html_and_json_default_paths.yaml)
+
+***
+
Change the expiration warning window to 1500 days (good for testing
the module out):
@@ -87,6 +160,15 @@ the module out):
- role: openshift_certificate_expiry
```
+```
+$ ansible-playbook -v -i HOSTS ./roles/openshift_certificate_expiry/examples/playbooks/longer_warning_period.yaml
+```
+
+
+> [View This Playbook](examples/playbooks/longer_warning_period.yaml)
+
+***
+
Change the expiration warning window to 1500 days (good for testing
the module out) and save the results as a JSON file:
@@ -103,9 +185,31 @@ the module out) and save the results as a JSON file:
- role: openshift_certificate_expiry
```
+```
+$ ansible-playbook -v -i HOSTS ./roles/openshift_certificate_expiry/examples/playbooks/longer-warning-period-json-results.yaml
+```
+
+
+> [View This Playbook](examples/playbooks/longer-warning-period-json-results.yaml)
+
-JSON Output
------------
+
+# Output Formats
+
+As noted above there are two ways to format your check report. In
+`json` format for machine parsing, or as a stylized `html` page for
+easy skimming. These options are shown below.
+
+## HTML Report
+
+![HTML Expiration Report](examples/cert-expiry-report-html.png)
+
+For an example of the HTML report you can browse, save
+[examples/cert-expiry-report.html](examples/cert-expiry-report.html)
+and then open the file in your browser.
+
+
+## JSON Report
There are two top-level keys in the saved JSON results, `data` and
`summary`.
@@ -122,85 +226,116 @@ certificates:
* expiring within the configured warning window
* already expired
-The example below is abbreviated to save space:
+For an example of the full JSON report, see [examples/cert-expiry-report.json](examples/cert-expiry-report.json).
+
+The example below is abbreviated to save space.
```json
{
- "data": {
- "192.168.124.148": {
- "etcd": [
- {
- "cert_cn": "CN:etcd-signer@1474563722",
- "days_remaining": 350,
- "expiry": "2017-09-22 17:02:25",
- "health": "warning",
- "path": "/etc/etcd/ca.crt"
- },
- ],
- "kubeconfigs": [
- {
- "cert_cn": "O:system:nodes, CN:system:node:m01.example.com",
- "days_remaining": 715,
- "expiry": "2018-09-22 17:08:57",
- "health": "warning",
- "path": "/etc/origin/node/system:node:m01.example.com.kubeconfig"
- },
- {
- "cert_cn": "O:system:cluster-admins, CN:system:admin",
- "days_remaining": 715,
- "expiry": "2018-09-22 17:04:40",
- "health": "warning",
- "path": "/etc/origin/master/admin.kubeconfig"
- }
- ],
- "meta": {
- "checked_at_time": "2016-10-07 15:26:47.608192",
- "show_all": "True",
- "warn_before_date": "2020-11-15 15:26:47.608192",
- "warning_days": 1500
- },
- "ocp_certs": [
- {
- "cert_cn": "CN:172.30.0.1, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:m01.example.com, DNS:openshift, DNS:openshift.default, DNS:openshift.default.svc, DNS:openshift.default.svc.cluster.local, DNS:172.30.0.1, DNS:192.168.124.148, IP Address:172.30.0.1, IP Address:192.168.124.148",
- "days_remaining": 715,
- "expiry": "2018-09-22 17:04:39",
- "health": "warning",
- "path": "/etc/origin/master/master.server.crt"
- },
- {
- "cert_cn": "CN:openshift-signer@1474563878",
- "days_remaining": 1810,
- "expiry": "2021-09-21 17:04:38",
- "health": "ok",
- "path": "/etc/origin/node/ca.crt"
- }
- ],
- "registry": [
- {
- "cert_cn": "CN:172.30.101.81, DNS:docker-registry-default.router.default.svc.cluster.local, DNS:docker-registry.default.svc.cluster.local, DNS:172.30.101.81, IP Address:172.30.101.81",
- "days_remaining": 728,
- "expiry": "2018-10-05 18:54:29",
- "health": "warning",
- "path": "/api/v1/namespaces/default/secrets/registry-certificates"
- }
- ],
- "router": [
- {
- "cert_cn": "CN:router.default.svc, DNS:router.default.svc, DNS:router.default.svc.cluster.local",
- "days_remaining": 715,
- "expiry": "2018-09-22 17:48:23",
- "health": "warning",
- "path": "/api/v1/namespaces/default/secrets/router-certs"
- }
- ]
+ "data": {
+ "m01.example.com": {
+ "etcd": [
+ {
+ "cert_cn": "CN:172.30.0.1, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc,...",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:00:03",
+ "health": "warning",
+ "path": "/etc/origin/master/etcd.server.crt",
+ "serial": 7,
+ "serial_hex": "0x7"
+ }
+ ],
+ "kubeconfigs": [
+ {
+ "cert_cn": "O:system:nodes, CN:system:node:m01.example.com",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:03:28",
+ "health": "warning",
+ "path": "/etc/origin/node/system:node:m01.example.com.kubeconfig",
+ "serial": 11,
+ "serial_hex": "0xb"
}
+ ],
+ "meta": {
+ "checked_at_time": "2017-01-17 10:36:25.230920",
+ "show_all": "True",
+ "warn_before_date": "2021-02-25 10:36:25.230920",
+ "warning_days": 1500
+ },
+ "ocp_certs": [
+ {
+ "cert_cn": "CN:172.30.0.1, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc,...",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:00:02",
+ "health": "warning",
+ "path": "/etc/origin/master/master.server.crt",
+ "serial": 4,
+ "serial_hex": "0x4"
+ }
+ ],
+ "registry": [
+ {
+ "cert_cn": "CN:172.30.242.251, DNS:docker-registry-default.router.default.svc.cluster.local,...",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:05:54",
+ "health": "warning",
+ "path": "/api/v1/namespaces/default/secrets/registry-certificates",
+ "serial": 13,
+ "serial_hex": "0xd"
+ }
+ ],
+ "router": [
+ {
+ "cert_cn": "CN:router.default.svc, DNS:router.default.svc, DNS:router.default.svc.cluster.local",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:05:46",
+ "health": "warning",
+ "path": "/api/v1/namespaces/default/secrets/router-certs",
+ "serial": 5050662940948454653,
+ "serial_hex": "0x46178f2f6b765cfd"
+ }
+ ]
},
- "summary": {
- "warning": 6,
- "expired": 0,
- "total": 7,
- "ok": 1
+ "n01.example.com": {
+ "etcd": [],
+ "kubeconfigs": [
+ {
+ "cert_cn": "O:system:nodes, CN:system:node:n01.example.com",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:03:28",
+ "health": "warning",
+ "path": "/etc/origin/node/system:node:n01.example.com.kubeconfig",
+ "serial": 11,
+ "serial_hex": "0xb"
+ }
+ ],
+ "meta": {
+ "checked_at_time": "2017-01-17 10:36:25.217103",
+ "show_all": "True",
+ "warn_before_date": "2021-02-25 10:36:25.217103",
+ "warning_days": 1500
+ },
+ "ocp_certs": [
+ {
+ "cert_cn": "CN:192.168.124.11, DNS:n01.example.com, DNS:192.168.124.11, IP Address:192.168.124.11",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:03:29",
+ "health": "warning",
+ "path": "/etc/origin/node/server.crt",
+ "serial": 12,
+ "serial_hex": "0xc"
+ }
+ ],
+ "registry": [],
+ "router": []
}
+ },
+ "summary": {
+ "expired": 0,
+ "ok": 3,
+ "total": 15,
+ "warning": 12
+ }
}
```
@@ -233,24 +368,17 @@ $ jq '.summary.warning,.summary.expired' /tmp/cert-expiry-report.json
```
-Requirements
-------------
-
+# Requirements
* None
-Dependencies
-------------
-
+# Dependencies
* None
-License
--------
-
+# License
Apache License, Version 2.0
-Author Information
-------------------
+# Author Information
Tim Bielawa (tbielawa@redhat.com)
diff --git a/roles/openshift_certificate_expiry/examples/cert-expiry-report-html.png b/roles/openshift_certificate_expiry/examples/cert-expiry-report-html.png
new file mode 100644
index 000000000..799131659
--- /dev/null
+++ b/roles/openshift_certificate_expiry/examples/cert-expiry-report-html.png
Binary files differ
diff --git a/roles/openshift_certificate_expiry/examples/cert-expiry-report.html b/roles/openshift_certificate_expiry/examples/cert-expiry-report.html
new file mode 100644
index 000000000..db03a5060
--- /dev/null
+++ b/roles/openshift_certificate_expiry/examples/cert-expiry-report.html
@@ -0,0 +1,396 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8" />
+ <title>OCP Certificate Expiry Report</title>
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
+ <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700" rel="stylesheet" />
+ <style type="text/css">
+ body {
+ font-family: 'Source Sans Pro', sans-serif;
+ margin-left: 50px;
+ margin-right: 50px;
+ margin-bottom: 20px;
+ padding-top: 70px;
+ }
+ table {
+ border-collapse: collapse;
+ margin-bottom: 20px;
+ }
+ table, th, td {
+ border: 1px solid black;
+ }
+ th, td {
+ padding: 5px;
+ }
+ .cert-kind {
+ margin-top: 5px;
+ margin-bottom: 5px;
+ }
+ footer {
+ font-size: small;
+ text-align: center;
+ }
+ tr.odd {
+ background-color: #f2f2f2;
+ }
+ </style>
+ </head>
+ <body>
+ <nav class="navbar navbar-default navbar-fixed-top">
+ <div class="container-fluid">
+ <div class="navbar-header">
+ <a class="navbar-brand" href="#">OCP Certificate Expiry Report</a>
+ </div>
+ <div class="collapse navbar-collapse">
+ <p class="navbar-text navbar-right">
+ <button>
+ <a href="https://docs.openshift.com/container-platform/latest/install_config/redeploying_certificates.html"
+ target="_blank"
+ class="navbar-link">
+ <i class="glyphicon glyphicon-book"></i> Redeploying Certificates
+ </a>
+ </button>
+ <button>
+ <a href="https://github.com/openshift/openshift-ansible/tree/master/roles/openshift_certificate_expiry"
+ target="_blank"
+ class="navbar-link">
+ <i class="glyphicon glyphicon-book"></i> Expiry Role Documentation
+ </a>
+ </button>
+ </p>
+ </div>
+ </div>
+ </nav>
+
+ <h1>m01.example.com</h1>
+
+ <p>
+ Checked 12 total certificates. Expired/Warning/OK: 0/10/2. Warning window: 1500 days
+ </p>
+ <ul>
+ <li><b>Expirations checked at:</b> 2017-01-17 10:36:25.230920</li>
+ <li><b>Warn after date:</b> 2021-02-25 10:36:25.230920</li>
+ </ul>
+
+ <table border="1" width="100%">
+ <tr>
+ <th colspan="7" style="text-align:center"><h2 class="cert-kind">ocp_certs</h2></th>
+ </tr>
+
+ <tr>
+ <th>&nbsp;</th>
+ <th style="width:33%">Certificate Common/Alt Name(s)</th>
+ <td>Serial</th>
+ <th>Health</th>
+ <th>Days Remaining</th>
+ <th>Expiration Date</th>
+ <th>Path</th>
+ </tr>
+
+
+ <tr class="odd">
+ <td style="text-align:center"><i class="glyphicon glyphicon-alert"></i></td>
+ <td style="width:33%">CN:172.30.0.1, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:m01.example.com, DNS:openshift, DNS:openshift.default, DNS:openshift.default.svc, DNS:openshift.default.svc.cluster.local, DNS:172.30.0.1, DNS:192.168.124.148, IP Address:172.30.0.1, IP Address:192.168.124.148</td>
+ <td><code>int(4)/hex(0x4)</code></td>
+ <td>warning</td>
+ <td>722</td>
+ <td>2019-01-09 17:00:02</td>
+ <td>/etc/origin/master/master.server.crt</td>
+ </tr>
+
+ <tr class="even">
+ <td style="text-align:center"><i class="glyphicon glyphicon-alert"></i></td>
+ <td style="width:33%">CN:192.168.124.148, DNS:m01.example.com, DNS:192.168.124.148, IP Address:192.168.124.148</td>
+ <td><code>int(12)/hex(0xc)</code></td>
+ <td>warning</td>
+ <td>722</td>
+ <td>2019-01-09 17:03:29</td>
+ <td>/etc/origin/node/server.crt</td>
+ </tr>
+
+ <tr class="odd">
+ <td style="text-align:center"><i class="glyphicon glyphicon-ok"></i></td>
+ <td style="width:33%">CN:openshift-signer@1483981200</td>
+ <td><code>int(1)/hex(0x1)</code></td>
+ <td>ok</td>
+ <td>1817</td>
+ <td>2022-01-08 17:00:01</td>
+ <td>/etc/origin/master/ca.crt</td>
+ </tr>
+
+ <tr class="even">
+ <td style="text-align:center"><i class="glyphicon glyphicon-ok"></i></td>
+ <td style="width:33%">CN:openshift-signer@1483981200</td>
+ <td><code>int(1)/hex(0x1)</code></td>
+ <td>ok</td>
+ <td>1817</td>
+ <td>2022-01-08 17:00:01</td>
+ <td>/etc/origin/node/ca.crt</td>
+ </tr>
+ <tr>
+ <th colspan="7" style="text-align:center"><h2 class="cert-kind">etcd</h2></th>
+ </tr>
+
+ <tr>
+ <th>&nbsp;</th>
+ <th style="width:33%">Certificate Common/Alt Name(s)</th>
+ <td>Serial</th>
+ <th>Health</th>
+ <th>Days Remaining</th>
+ <th>Expiration Date</th>
+ <th>Path</th>
+ </tr>
+
+
+ <tr class="odd">
+ <td style="text-align:center"><i class="glyphicon glyphicon-alert"></i></td>
+ <td style="width:33%">CN:172.30.0.1, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:m01.example.com, DNS:openshift, DNS:openshift.default, DNS:openshift.default.svc, DNS:openshift.default.svc.cluster.local, DNS:172.30.0.1, DNS:192.168.124.148, IP Address:172.30.0.1, IP Address:192.168.124.148</td>
+ <td><code>int(7)/hex(0x7)</code></td>
+ <td>warning</td>
+ <td>722</td>
+ <td>2019-01-09 17:00:03</td>
+ <td>/etc/origin/master/etcd.server.crt</td>
+ </tr>
+ <tr>
+ <th colspan="7" style="text-align:center"><h2 class="cert-kind">kubeconfigs</h2></th>
+ </tr>
+
+ <tr>
+ <th>&nbsp;</th>
+ <th style="width:33%">Certificate Common/Alt Name(s)</th>
+ <td>Serial</th>
+ <th>Health</th>
+ <th>Days Remaining</th>
+ <th>Expiration Date</th>
+ <th>Path</th>
+ </tr>
+
+
+ <tr class="odd">
+ <td style="text-align:center"><i class="glyphicon glyphicon-alert"></i></td>
+ <td style="width:33%">O:system:nodes, CN:system:node:m01.example.com</td>
+ <td><code>int(11)/hex(0xb)</code></td>
+ <td>warning</td>
+ <td>722</td>
+ <td>2019-01-09 17:03:28</td>
+ <td>/etc/origin/node/system:node:m01.example.com.kubeconfig</td>
+ </tr>
+
+ <tr class="even">
+ <td style="text-align:center"><i class="glyphicon glyphicon-alert"></i></td>
+ <td style="width:33%">O:system:cluster-admins, CN:system:admin</td>
+ <td><code>int(8)/hex(0x8)</code></td>
+ <td>warning</td>
+ <td>722</td>
+ <td>2019-01-09 17:00:03</td>
+ <td>/etc/origin/master/admin.kubeconfig</td>
+ </tr>
+
+ <tr class="odd">
+ <td style="text-align:center"><i class="glyphicon glyphicon-alert"></i></td>
+ <td style="width:33%">O:system:masters, CN:system:openshift-master</td>
+ <td><code>int(3)/hex(0x3)</code></td>
+ <td>warning</td>
+ <td>722</td>
+ <td>2019-01-09 17:00:02</td>
+ <td>/etc/origin/master/openshift-master.kubeconfig</td>
+ </tr>
+
+ <tr class="even">
+ <td style="text-align:center"><i class="glyphicon glyphicon-alert"></i></td>
+ <td style="width:33%">O:system:routers, CN:system:openshift-router</td>
+ <td><code>int(9)/hex(0x9)</code></td>
+ <td>warning</td>
+ <td>722</td>
+ <td>2019-01-09 17:00:03</td>
+ <td>/etc/origin/master/openshift-router.kubeconfig</td>
+ </tr>
+
+ <tr class="odd">
+ <td style="text-align:center"><i class="glyphicon glyphicon-alert"></i></td>
+ <td style="width:33%">O:system:registries, CN:system:openshift-registry</td>
+ <td><code>int(10)/hex(0xa)</code></td>
+ <td>warning</td>
+ <td>722</td>
+ <td>2019-01-09 17:00:03</td>
+ <td>/etc/origin/master/openshift-registry.kubeconfig</td>
+ </tr>
+ <tr>
+ <th colspan="7" style="text-align:center"><h2 class="cert-kind">router</h2></th>
+ </tr>
+
+ <tr>
+ <th>&nbsp;</th>
+ <th style="width:33%">Certificate Common/Alt Name(s)</th>
+ <td>Serial</th>
+ <th>Health</th>
+ <th>Days Remaining</th>
+ <th>Expiration Date</th>
+ <th>Path</th>
+ </tr>
+
+
+ <tr class="odd">
+ <td style="text-align:center"><i class="glyphicon glyphicon-alert"></i></td>
+ <td style="width:33%">CN:router.default.svc, DNS:router.default.svc, DNS:router.default.svc.cluster.local</td>
+ <td><code>int(5050662940948454653)/hex(0x46178f2f6b765cfd)</code></td>
+ <td>warning</td>
+ <td>722</td>
+ <td>2019-01-09 17:05:46</td>
+ <td>/api/v1/namespaces/default/secrets/router-certs</td>
+ </tr>
+ <tr>
+ <th colspan="7" style="text-align:center"><h2 class="cert-kind">registry</h2></th>
+ </tr>
+
+ <tr>
+ <th>&nbsp;</th>
+ <th style="width:33%">Certificate Common/Alt Name(s)</th>
+ <td>Serial</th>
+ <th>Health</th>
+ <th>Days Remaining</th>
+ <th>Expiration Date</th>
+ <th>Path</th>
+ </tr>
+
+
+ <tr class="odd">
+ <td style="text-align:center"><i class="glyphicon glyphicon-alert"></i></td>
+ <td style="width:33%">CN:172.30.242.251, DNS:docker-registry-default.router.default.svc.cluster.local, DNS:docker-registry.default.svc.cluster.local, DNS:172.30.242.251, IP Address:172.30.242.251</td>
+ <td><code>int(13)/hex(0xd)</code></td>
+ <td>warning</td>
+ <td>722</td>
+ <td>2019-01-09 17:05:54</td>
+ <td>/api/v1/namespaces/default/secrets/registry-certificates</td>
+ </tr>
+ </table>
+ <hr />
+ <h1>n01.example.com</h1>
+
+ <p>
+ Checked 3 total certificates. Expired/Warning/OK: 0/2/1. Warning window: 1500 days
+ </p>
+ <ul>
+ <li><b>Expirations checked at:</b> 2017-01-17 10:36:25.217103</li>
+ <li><b>Warn after date:</b> 2021-02-25 10:36:25.217103</li>
+ </ul>
+
+ <table border="1" width="100%">
+ <tr>
+ <th colspan="7" style="text-align:center"><h2 class="cert-kind">ocp_certs</h2></th>
+ </tr>
+
+ <tr>
+ <th>&nbsp;</th>
+ <th style="width:33%">Certificate Common/Alt Name(s)</th>
+ <td>Serial</th>
+ <th>Health</th>
+ <th>Days Remaining</th>
+ <th>Expiration Date</th>
+ <th>Path</th>
+ </tr>
+
+
+ <tr class="odd">
+ <td style="text-align:center"><i class="glyphicon glyphicon-alert"></i></td>
+ <td style="width:33%">CN:192.168.124.11, DNS:n01.example.com, DNS:192.168.124.11, IP Address:192.168.124.11</td>
+ <td><code>int(12)/hex(0xc)</code></td>
+ <td>warning</td>
+ <td>722</td>
+ <td>2019-01-09 17:03:29</td>
+ <td>/etc/origin/node/server.crt</td>
+ </tr>
+
+ <tr class="even">
+ <td style="text-align:center"><i class="glyphicon glyphicon-ok"></i></td>
+ <td style="width:33%">CN:openshift-signer@1483981200</td>
+ <td><code>int(1)/hex(0x1)</code></td>
+ <td>ok</td>
+ <td>1817</td>
+ <td>2022-01-08 17:00:01</td>
+ <td>/etc/origin/node/ca.crt</td>
+ </tr>
+ <tr>
+ <th colspan="7" style="text-align:center"><h2 class="cert-kind">etcd</h2></th>
+ </tr>
+
+ <tr>
+ <th>&nbsp;</th>
+ <th style="width:33%">Certificate Common/Alt Name(s)</th>
+ <td>Serial</th>
+ <th>Health</th>
+ <th>Days Remaining</th>
+ <th>Expiration Date</th>
+ <th>Path</th>
+ </tr>
+
+ <tr>
+ <th colspan="7" style="text-align:center"><h2 class="cert-kind">kubeconfigs</h2></th>
+ </tr>
+
+ <tr>
+ <th>&nbsp;</th>
+ <th style="width:33%">Certificate Common/Alt Name(s)</th>
+ <td>Serial</th>
+ <th>Health</th>
+ <th>Days Remaining</th>
+ <th>Expiration Date</th>
+ <th>Path</th>
+ </tr>
+
+
+ <tr class="odd">
+ <td style="text-align:center"><i class="glyphicon glyphicon-alert"></i></td>
+ <td style="width:33%">O:system:nodes, CN:system:node:n01.example.com</td>
+ <td><code>int(11)/hex(0xb)</code></td>
+ <td>warning</td>
+ <td>722</td>
+ <td>2019-01-09 17:03:28</td>
+ <td>/etc/origin/node/system:node:n01.example.com.kubeconfig</td>
+ </tr>
+ <tr>
+ <th colspan="7" style="text-align:center"><h2 class="cert-kind">router</h2></th>
+ </tr>
+
+ <tr>
+ <th>&nbsp;</th>
+ <th style="width:33%">Certificate Common/Alt Name(s)</th>
+ <td>Serial</th>
+ <th>Health</th>
+ <th>Days Remaining</th>
+ <th>Expiration Date</th>
+ <th>Path</th>
+ </tr>
+
+ <tr>
+ <th colspan="7" style="text-align:center"><h2 class="cert-kind">registry</h2></th>
+ </tr>
+
+ <tr>
+ <th>&nbsp;</th>
+ <th style="width:33%">Certificate Common/Alt Name(s)</th>
+ <td>Serial</th>
+ <th>Health</th>
+ <th>Days Remaining</th>
+ <th>Expiration Date</th>
+ <th>Path</th>
+ </tr>
+
+ </table>
+ <hr />
+
+ <footer>
+ <p>
+ Expiration report generated by
+ the <a href="https://github.com/openshift/openshift-ansible"
+ target="_blank">openshift-ansible</a>
+ <a href="https://github.com/openshift/openshift-ansible/tree/master/roles/openshift_certificate_expiry"
+ target="_blank">certificate expiry</a> role.
+ </p>
+ <p>
+ Status icons from bootstrap/glyphicon
+ </p>
+ </footer>
+ </body>
+</html>
diff --git a/roles/openshift_certificate_expiry/examples/cert-expiry-report.json b/roles/openshift_certificate_expiry/examples/cert-expiry-report.json
new file mode 100644
index 000000000..8206e2842
--- /dev/null
+++ b/roles/openshift_certificate_expiry/examples/cert-expiry-report.json
@@ -0,0 +1,178 @@
+{
+ "data": {
+ "m01.example.com": {
+ "etcd": [
+ {
+ "cert_cn": "CN:172.30.0.1, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:m01.example.com, DNS:openshift, DNS:openshift.default, DNS:openshift.default.svc, DNS:openshift.default.svc.cluster.local, DNS:172.30.0.1, DNS:192.168.124.148, IP Address:172.30.0.1, IP Address:192.168.124.148",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:00:03",
+ "health": "warning",
+ "path": "/etc/origin/master/etcd.server.crt",
+ "serial": 7,
+ "serial_hex": "0x7"
+ }
+ ],
+ "kubeconfigs": [
+ {
+ "cert_cn": "O:system:nodes, CN:system:node:m01.example.com",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:03:28",
+ "health": "warning",
+ "path": "/etc/origin/node/system:node:m01.example.com.kubeconfig",
+ "serial": 11,
+ "serial_hex": "0xb"
+ },
+ {
+ "cert_cn": "O:system:cluster-admins, CN:system:admin",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:00:03",
+ "health": "warning",
+ "path": "/etc/origin/master/admin.kubeconfig",
+ "serial": 8,
+ "serial_hex": "0x8"
+ },
+ {
+ "cert_cn": "O:system:masters, CN:system:openshift-master",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:00:02",
+ "health": "warning",
+ "path": "/etc/origin/master/openshift-master.kubeconfig",
+ "serial": 3,
+ "serial_hex": "0x3"
+ },
+ {
+ "cert_cn": "O:system:routers, CN:system:openshift-router",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:00:03",
+ "health": "warning",
+ "path": "/etc/origin/master/openshift-router.kubeconfig",
+ "serial": 9,
+ "serial_hex": "0x9"
+ },
+ {
+ "cert_cn": "O:system:registries, CN:system:openshift-registry",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:00:03",
+ "health": "warning",
+ "path": "/etc/origin/master/openshift-registry.kubeconfig",
+ "serial": 10,
+ "serial_hex": "0xa"
+ }
+ ],
+ "meta": {
+ "checked_at_time": "2017-01-17 10:36:25.230920",
+ "show_all": "True",
+ "warn_before_date": "2021-02-25 10:36:25.230920",
+ "warning_days": 1500
+ },
+ "ocp_certs": [
+ {
+ "cert_cn": "CN:172.30.0.1, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:m01.example.com, DNS:openshift, DNS:openshift.default, DNS:openshift.default.svc, DNS:openshift.default.svc.cluster.local, DNS:172.30.0.1, DNS:192.168.124.148, IP Address:172.30.0.1, IP Address:192.168.124.148",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:00:02",
+ "health": "warning",
+ "path": "/etc/origin/master/master.server.crt",
+ "serial": 4,
+ "serial_hex": "0x4"
+ },
+ {
+ "cert_cn": "CN:192.168.124.148, DNS:m01.example.com, DNS:192.168.124.148, IP Address:192.168.124.148",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:03:29",
+ "health": "warning",
+ "path": "/etc/origin/node/server.crt",
+ "serial": 12,
+ "serial_hex": "0xc"
+ },
+ {
+ "cert_cn": "CN:openshift-signer@1483981200",
+ "days_remaining": 1817,
+ "expiry": "2022-01-08 17:00:01",
+ "health": "ok",
+ "path": "/etc/origin/master/ca.crt",
+ "serial": 1,
+ "serial_hex": "0x1"
+ },
+ {
+ "cert_cn": "CN:openshift-signer@1483981200",
+ "days_remaining": 1817,
+ "expiry": "2022-01-08 17:00:01",
+ "health": "ok",
+ "path": "/etc/origin/node/ca.crt",
+ "serial": 1,
+ "serial_hex": "0x1"
+ }
+ ],
+ "registry": [
+ {
+ "cert_cn": "CN:172.30.242.251, DNS:docker-registry-default.router.default.svc.cluster.local, DNS:docker-registry.default.svc.cluster.local, DNS:172.30.242.251, IP Address:172.30.242.251",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:05:54",
+ "health": "warning",
+ "path": "/api/v1/namespaces/default/secrets/registry-certificates",
+ "serial": 13,
+ "serial_hex": "0xd"
+ }
+ ],
+ "router": [
+ {
+ "cert_cn": "CN:router.default.svc, DNS:router.default.svc, DNS:router.default.svc.cluster.local",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:05:46",
+ "health": "warning",
+ "path": "/api/v1/namespaces/default/secrets/router-certs",
+ "serial": 5050662940948454653,
+ "serial_hex": "0x46178f2f6b765cfd"
+ }
+ ]
+ },
+ "n01.example.com": {
+ "etcd": [],
+ "kubeconfigs": [
+ {
+ "cert_cn": "O:system:nodes, CN:system:node:n01.example.com",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:03:28",
+ "health": "warning",
+ "path": "/etc/origin/node/system:node:n01.example.com.kubeconfig",
+ "serial": 11,
+ "serial_hex": "0xb"
+ }
+ ],
+ "meta": {
+ "checked_at_time": "2017-01-17 10:36:25.217103",
+ "show_all": "True",
+ "warn_before_date": "2021-02-25 10:36:25.217103",
+ "warning_days": 1500
+ },
+ "ocp_certs": [
+ {
+ "cert_cn": "CN:192.168.124.11, DNS:n01.example.com, DNS:192.168.124.11, IP Address:192.168.124.11",
+ "days_remaining": 722,
+ "expiry": "2019-01-09 17:03:29",
+ "health": "warning",
+ "path": "/etc/origin/node/server.crt",
+ "serial": 12,
+ "serial_hex": "0xc"
+ },
+ {
+ "cert_cn": "CN:openshift-signer@1483981200",
+ "days_remaining": 1817,
+ "expiry": "2022-01-08 17:00:01",
+ "health": "ok",
+ "path": "/etc/origin/node/ca.crt",
+ "serial": 1,
+ "serial_hex": "0x1"
+ }
+ ],
+ "registry": [],
+ "router": []
+ }
+ },
+ "summary": {
+ "expired": 0,
+ "ok": 3,
+ "total": 15,
+ "warning": 12
+ }
+}
diff --git a/roles/openshift_certificate_expiry/examples/playbooks/default.yaml b/roles/openshift_certificate_expiry/examples/playbooks/default.yaml
new file mode 100644
index 000000000..630135cae
--- /dev/null
+++ b/roles/openshift_certificate_expiry/examples/playbooks/default.yaml
@@ -0,0 +1,10 @@
+---
+# Default behavior, you will need to ensure you run ansible with the
+# -v option to see report results:
+
+- name: Check cert expirys
+ hosts: nodes:masters:etcd
+ become: yes
+ gather_facts: no
+ roles:
+ - role: openshift_certificate_expiry
diff --git a/roles/openshift_certificate_expiry/examples/playbooks/easy-mode.yaml b/roles/openshift_certificate_expiry/examples/playbooks/easy-mode.yaml
new file mode 100644
index 000000000..d0209426f
--- /dev/null
+++ b/roles/openshift_certificate_expiry/examples/playbooks/easy-mode.yaml
@@ -0,0 +1,21 @@
+---
+# This example playbook is great if you're just wanting to try the
+# role out.
+#
+# This example enables HTML and JSON reports
+#
+# The warning window is set very large so you will almost always get results back
+#
+# All certificates (healthy or not) are included in the results
+
+- name: Check cert expirys
+ hosts: nodes:masters:etcd
+ become: yes
+ gather_facts: no
+ vars:
+ openshift_certificate_expiry_warning_days: 1500
+ openshift_certificate_expiry_save_json_results: yes
+ openshift_certificate_expiry_generate_html_report: yes
+ openshift_certificate_expiry_show_all: yes
+ roles:
+ - role: openshift_certificate_expiry
diff --git a/roles/openshift_certificate_expiry/examples/playbooks/html_and_json_default_paths.yaml b/roles/openshift_certificate_expiry/examples/playbooks/html_and_json_default_paths.yaml
new file mode 100644
index 000000000..d80cb6ff4
--- /dev/null
+++ b/roles/openshift_certificate_expiry/examples/playbooks/html_and_json_default_paths.yaml
@@ -0,0 +1,12 @@
+---
+# Generate HTML and JSON artifacts in their default paths:
+
+- name: Check cert expirys
+ hosts: nodes:masters:etcd
+ become: yes
+ gather_facts: no
+ vars:
+ openshift_certificate_expiry_generate_html_report: yes
+ openshift_certificate_expiry_save_json_results: yes
+ roles:
+ - role: openshift_certificate_expiry
diff --git a/roles/openshift_certificate_expiry/examples/playbooks/longer-warning-period-json-results.yaml b/roles/openshift_certificate_expiry/examples/playbooks/longer-warning-period-json-results.yaml
new file mode 100644
index 000000000..87a0f3be4
--- /dev/null
+++ b/roles/openshift_certificate_expiry/examples/playbooks/longer-warning-period-json-results.yaml
@@ -0,0 +1,13 @@
+---
+# Change the expiration warning window to 1500 days (good for testing
+# the module out) and save the results as a JSON file:
+
+- name: Check cert expirys
+ hosts: nodes:masters:etcd
+ become: yes
+ gather_facts: no
+ vars:
+ openshift_certificate_expiry_warning_days: 1500
+ openshift_certificate_expiry_save_json_results: yes
+ roles:
+ - role: openshift_certificate_expiry
diff --git a/roles/openshift_certificate_expiry/examples/playbooks/longer_warning_period.yaml b/roles/openshift_certificate_expiry/examples/playbooks/longer_warning_period.yaml
new file mode 100644
index 000000000..960457c4b
--- /dev/null
+++ b/roles/openshift_certificate_expiry/examples/playbooks/longer_warning_period.yaml
@@ -0,0 +1,12 @@
+---
+# Change the expiration warning window to 1500 days (good for testing
+# the module out):
+
+- name: Check cert expirys
+ hosts: nodes:masters:etcd
+ become: yes
+ gather_facts: no
+ vars:
+ openshift_certificate_expiry_warning_days: 1500
+ roles:
+ - role: openshift_certificate_expiry
diff --git a/roles/openshift_certificate_expiry/library/openshift_cert_expiry.py b/roles/openshift_certificate_expiry/library/openshift_cert_expiry.py
index a474b36b0..85671b164 100644
--- a/roles/openshift_certificate_expiry/library/openshift_cert_expiry.py
+++ b/roles/openshift_certificate_expiry/library/openshift_cert_expiry.py
@@ -122,6 +122,8 @@ A 3-tuple of the form: (certificate_common_name, certificate_expiry_date, certif
cert_loaded = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, _cert_string)
+ cert_serial = cert_loaded.get_serial_number()
+
######################################################################
# Read all possible names from the cert
cert_subjects = []
@@ -178,7 +180,7 @@ A 3-tuple of the form: (certificate_common_name, certificate_expiry_date, certif
time_remaining = cert_expiry_date - now
- return (cert_subject, cert_expiry_date, time_remaining)
+ return (cert_subject, cert_expiry_date, time_remaining, cert_serial)
def classify_cert(cert_meta, now, time_remaining, expire_window, cert_list):
@@ -210,6 +212,7 @@ Return:
cert_meta['health'] = 'ok'
cert_meta['expiry'] = expiry_str
+ cert_meta['serial_hex'] = hex(int(cert_meta['serial']))
cert_list.append(cert_meta)
return cert_list
@@ -373,7 +376,10 @@ an OpenShift Container Platform cluster
for _, v in cert_meta.items():
with open(v, 'r') as fp:
cert = fp.read()
- cert_subject, cert_expiry_date, time_remaining = load_and_handle_cert(cert, now)
+ (cert_subject,
+ cert_expiry_date,
+ time_remaining,
+ cert_serial) = load_and_handle_cert(cert, now)
expire_check_result = {
'cert_cn': cert_subject,
@@ -381,6 +387,7 @@ an OpenShift Container Platform cluster
'expiry': cert_expiry_date,
'days_remaining': time_remaining.days,
'health': None,
+ 'serial': cert_serial
}
classify_cert(expire_check_result, now, time_remaining, expire_window, ocp_certs)
@@ -420,7 +427,8 @@ an OpenShift Container Platform cluster
c = cfg['users'][0]['user']['client-certificate-data']
(cert_subject,
cert_expiry_date,
- time_remaining) = load_and_handle_cert(c, now, base64decode=True)
+ time_remaining,
+ cert_serial) = load_and_handle_cert(c, now, base64decode=True)
expire_check_result = {
'cert_cn': cert_subject,
@@ -428,6 +436,7 @@ an OpenShift Container Platform cluster
'expiry': cert_expiry_date,
'days_remaining': time_remaining.days,
'health': None,
+ 'serial': cert_serial
}
classify_cert(expire_check_result, now, time_remaining, expire_window, kubeconfigs)
@@ -448,7 +457,8 @@ an OpenShift Container Platform cluster
c = cfg['users'][0]['user']['client-certificate-data']
(cert_subject,
cert_expiry_date,
- time_remaining) = load_and_handle_cert(c, now, base64decode=True)
+ time_remaining,
+ cert_serial) = load_and_handle_cert(c, now, base64decode=True)
expire_check_result = {
'cert_cn': cert_subject,
@@ -456,6 +466,7 @@ an OpenShift Container Platform cluster
'expiry': cert_expiry_date,
'days_remaining': time_remaining.days,
'health': None,
+ 'serial': cert_serial
}
classify_cert(expire_check_result, now, time_remaining, expire_window, kubeconfigs)
@@ -500,7 +511,8 @@ an OpenShift Container Platform cluster
c = fp.read()
(cert_subject,
cert_expiry_date,
- time_remaining) = load_and_handle_cert(c, now)
+ time_remaining,
+ cert_serial) = load_and_handle_cert(c, now)
expire_check_result = {
'cert_cn': cert_subject,
@@ -508,6 +520,7 @@ an OpenShift Container Platform cluster
'expiry': cert_expiry_date,
'days_remaining': time_remaining.days,
'health': None,
+ 'serial': cert_serial
}
classify_cert(expire_check_result, now, time_remaining, expire_window, etcd_certs)
@@ -537,7 +550,8 @@ an OpenShift Container Platform cluster
with open(etcd_cert, 'r') as etcd_fp:
(cert_subject,
cert_expiry_date,
- time_remaining) = load_and_handle_cert(etcd_fp.read(), now)
+ time_remaining,
+ cert_serial) = load_and_handle_cert(etcd_fp.read(), now)
expire_check_result = {
'cert_cn': cert_subject,
@@ -545,6 +559,7 @@ an OpenShift Container Platform cluster
'expiry': cert_expiry_date,
'days_remaining': time_remaining.days,
'health': None,
+ 'serial': cert_serial
}
classify_cert(expire_check_result, now, time_remaining, expire_window, etcd_certs)
@@ -581,7 +596,8 @@ an OpenShift Container Platform cluster
else:
(cert_subject,
cert_expiry_date,
- time_remaining) = load_and_handle_cert(router_c, now, base64decode=True)
+ time_remaining,
+ cert_serial) = load_and_handle_cert(router_c, now, base64decode=True)
expire_check_result = {
'cert_cn': cert_subject,
@@ -589,6 +605,7 @@ an OpenShift Container Platform cluster
'expiry': cert_expiry_date,
'days_remaining': time_remaining.days,
'health': None,
+ 'serial': cert_serial
}
classify_cert(expire_check_result, now, time_remaining, expire_window, router_certs)
@@ -610,7 +627,8 @@ an OpenShift Container Platform cluster
else:
(cert_subject,
cert_expiry_date,
- time_remaining) = load_and_handle_cert(registry_c, now, base64decode=True)
+ time_remaining,
+ cert_serial) = load_and_handle_cert(registry_c, now, base64decode=True)
expire_check_result = {
'cert_cn': cert_subject,
@@ -618,6 +636,7 @@ an OpenShift Container Platform cluster
'expiry': cert_expiry_date,
'days_remaining': time_remaining.days,
'health': None,
+ 'serial': cert_serial
}
classify_cert(expire_check_result, now, time_remaining, expire_window, registry_certs)
diff --git a/roles/openshift_certificate_expiry/templates/cert-expiry-table.html.j2 b/roles/openshift_certificate_expiry/templates/cert-expiry-table.html.j2
index b05110336..1d4bb24e9 100644
--- a/roles/openshift_certificate_expiry/templates/cert-expiry-table.html.j2
+++ b/roles/openshift_certificate_expiry/templates/cert-expiry-table.html.j2
@@ -45,11 +45,20 @@
</div>
<div class="collapse navbar-collapse">
<p class="navbar-text navbar-right">
- <a href="https://docs.openshift.com/container-platform/latest/install_config/redeploying_certificates.html"
- target="_blank"
- class="navbar-link">
- <i class="glyphicon glyphicon-book"></i> Redeploying Certificates
- </a>
+ <button>
+ <a href="https://docs.openshift.com/container-platform/latest/install_config/redeploying_certificates.html"
+ target="_blank"
+ class="navbar-link">
+ <i class="glyphicon glyphicon-book"></i> Redeploying Certificates
+ </a>
+ </button>
+ <button>
+ <a href="https://github.com/openshift/openshift-ansible/tree/master/roles/openshift_certificate_expiry"
+ target="_blank"
+ class="navbar-link">
+ <i class="glyphicon glyphicon-book"></i> Expiry Role Documentation
+ </a>
+ </button>
</p>
</div>
</div>
@@ -71,12 +80,13 @@
{# These are hard-coded right now, but should be grabbed dynamically from the registered results #}
{%- for kind in ['ocp_certs', 'etcd', 'kubeconfigs', 'router', 'registry'] -%}
<tr>
- <th colspan="6" style="text-align:center"><h2 class="cert-kind">{{ kind }}</h2></th>
+ <th colspan="7" style="text-align:center"><h2 class="cert-kind">{{ kind }}</h2></th>
</tr>
<tr>
<th>&nbsp;</th>
<th style="width:33%">Certificate Common/Alt Name(s)</th>
+ <td>Serial</th>
<th>Health</th>
<th>Days Remaining</th>
<th>Expiration Date</th>
@@ -98,6 +108,7 @@
<tr class="{{ loop.cycle('odd', 'even') }}">
<td style="text-align:center"><i class="{{ health_icon }}"></i></td>
<td style="width:33%">{{ v.cert_cn }}</td>
+ <td><code>int({{ v.serial }})/hex({{ v.serial_hex }})</code></td>
<td>{{ v.health }}</td>
<td>{{ v.days_remaining }}</td>
<td>{{ v.expiry }}</td>
@@ -114,7 +125,11 @@
<footer>
<p>
- Expiration report generated by <a href="https://github.com/openshift/openshift-ansible" target="_blank">openshift-ansible</a>
+ Expiration report generated by
+ the <a href="https://github.com/openshift/openshift-ansible"
+ target="_blank">openshift-ansible</a>
+ <a href="https://github.com/openshift/openshift-ansible/tree/master/roles/openshift_certificate_expiry"
+ target="_blank">certificate expiry</a> role.
</p>
<p>
Status icons from bootstrap/glyphicon