glusterfs/0116-eventsapi-HTTPS-support-for-Webhooks.patch
Milind Changire cf62f1947f autobuild v3.12.2-2
Resolves: bz#1264911 bz#1277924 bz#1286820 bz#1360331 bz#1401969
Resolves: bz#1410719 bz#1419438 bz#1426042 bz#1444820 bz#1459101
Resolves: bz#1464150 bz#1464350 bz#1466122 bz#1466129 bz#1467903
Resolves: bz#1468972 bz#1476876 bz#1484446 bz#1492591 bz#1498391
Resolves: bz#1498730 bz#1499865 bz#1500704 bz#1501345 bz#1505570
Resolves: bz#1507361 bz#1507394 bz#1509102 bz#1509191 bz#1509810
Resolves: bz#1509833 bz#1511766 bz#1512470 bz#1512496 bz#1512963
Resolves: bz#1515051 bz#1519076 bz#1519740 bz#1534253 bz#1534530
Signed-off-by: Milind Changire <mchangir@redhat.com>
2018-01-17 02:21:37 -05:00

243 lines
9.6 KiB
Diff

From cee93742430f0ecd3defb65e5ca62ef37f581703 Mon Sep 17 00:00:00 2001
From: Aravinda VK <avishwan@redhat.com>
Date: Tue, 17 Oct 2017 12:50:48 +0530
Subject: [PATCH 116/128] eventsapi: HTTPS support for Webhooks
First it tries to call URL with verify=True without specifying the cert
path, it succeeds if a webhook is HTTP or HTTPS with CA trusted
certificates(for example https://github..).
If above call fails with SSL error then it tries to get the server
certificate and calls URL again. If call fails with SSL error even after
using the certificate, then verification will be disabled and logged in
the log file.
All other errors will be catched and logged as usual.
>upstream mainline patch : https://review.gluster.org/18578
BUG: 1466122
Change-Id: I86a3390ed48b75dffdc7848022af23a1e1d7f076
Signed-off-by: Aravinda VK <avishwan@redhat.com>
Reviewed-on: https://code.engineering.redhat.com/gerrit/126618
Tested-by: RHGS Build Bot <nigelb@redhat.com>
Reviewed-by: Atin Mukherjee <amukherj@redhat.com>
---
events/src/eventsapiconf.py.in | 1 +
events/src/peer_eventsapi.py | 48 +++++++++++++++++----
events/src/utils.py | 94 ++++++++++++++++++++++++++++++++----------
3 files changed, 114 insertions(+), 29 deletions(-)
diff --git a/events/src/eventsapiconf.py.in b/events/src/eventsapiconf.py.in
index 08a3602..687eaa3 100644
--- a/events/src/eventsapiconf.py.in
+++ b/events/src/eventsapiconf.py.in
@@ -26,6 +26,7 @@ UUID_FILE = "@GLUSTERD_WORKDIR@/glusterd.info"
PID_FILE = "@localstatedir@/run/glustereventsd.pid"
AUTO_BOOL_ATTRIBUTES = ["force", "push-pem", "no-verify"]
AUTO_INT_ATTRIBUTES = ["ssh-port"]
+CERTS_DIR = "@GLUSTERD_WORKDIR@/events"
# Errors
ERROR_SAME_CONFIG = 2
diff --git a/events/src/peer_eventsapi.py b/events/src/peer_eventsapi.py
index 3a6a0eb..d72fdbe 100644
--- a/events/src/peer_eventsapi.py
+++ b/events/src/peer_eventsapi.py
@@ -27,7 +27,7 @@ from gluster.cliutils import (Cmd, node_output_ok, node_output_notok,
sync_file_to_peers, GlusterCmdException,
output_error, execute_in_peers, runcli,
set_common_args_func)
-from events.utils import LockedOpen, get_jwt_token
+from events.utils import LockedOpen, get_jwt_token, save_https_cert
from events.eventsapiconf import (WEBHOOKS_FILE_TO_SYNC,
WEBHOOKS_FILE,
@@ -47,7 +47,8 @@ from events.eventsapiconf import (WEBHOOKS_FILE_TO_SYNC,
ERROR_PARTIAL_SUCCESS,
ERROR_ALL_NODES_STATUS_NOT_OK,
ERROR_SAME_CONFIG,
- ERROR_WEBHOOK_SYNC_FAILED)
+ ERROR_WEBHOOK_SYNC_FAILED,
+ CERTS_DIR)
def handle_output_error(err, errcode=1, json_output=False):
@@ -405,12 +406,43 @@ class NodeWebhookTestCmd(Cmd):
if hashval:
http_headers["Authorization"] = "Bearer " + hashval
- try:
- resp = requests.post(args.url, headers=http_headers)
- except requests.ConnectionError as e:
- node_output_notok("{0}".format(e))
- except requests.exceptions.InvalidSchema as e:
- node_output_notok("{0}".format(e))
+ urldata = requests.utils.urlparse(args.url)
+ parts = urldata.netloc.split(":")
+ domain = parts[0]
+ # Default https port if not specified
+ port = 443
+ if len(parts) == 2:
+ port = int(parts[1])
+
+ cert_path = os.path.join(CERTS_DIR, args.url.replace("/", "_").strip())
+ verify = True
+ while True:
+ try:
+ resp = requests.post(args.url, headers=http_headers,
+ verify=verify)
+ # Successful webhook push
+ break
+ except requests.exceptions.SSLError as e:
+ # If verify is equal to cert path, but still failed with
+ # SSLError, Looks like some issue with custom downloaded
+ # certificate, Try with verify = false
+ if verify == cert_path:
+ verify = False
+ continue
+
+ # If verify is instance of bool and True, then custom cert
+ # is required, download the cert and retry
+ try:
+ save_https_cert(domain, port, cert_path)
+ verify = cert_path
+ except Exception:
+ verify = False
+
+ # Done with collecting cert, continue
+ continue
+ except Exception as e:
+ node_output_notok("{0}".format(e))
+ break
if resp.status_code != 200:
node_output_notok("{0}".format(resp.status_code))
diff --git a/events/src/utils.py b/events/src/utils.py
index f24d64d..f405e44 100644
--- a/events/src/utils.py
+++ b/events/src/utils.py
@@ -27,7 +27,8 @@ from eventsapiconf import (LOG_FILE,
WEBHOOKS_FILE,
DEFAULT_CONFIG_FILE,
CUSTOM_CONFIG_FILE,
- UUID_FILE)
+ UUID_FILE,
+ CERTS_DIR)
import eventtypes
@@ -209,11 +210,33 @@ def get_jwt_token(secret, event_type, event_ts, jwt_expiry_time_seconds=60):
)
+def save_https_cert(domain, port, cert_path):
+ import ssl
+
+ # Cert file already available for this URL
+ if os.path.exists(cert_path):
+ return
+
+ cert_data = ssl.get_server_certificate((domain, port))
+ with open(cert_path, "w") as f:
+ f.write(cert_data)
+
+
def publish_to_webhook(url, token, secret, message_queue):
# Import requests here since not used in any other place
import requests
http_headers = {"Content-Type": "application/json"}
+ urldata = requests.utils.urlparse(url)
+ parts = urldata.netloc.split(":")
+ domain = parts[0]
+ # Default https port if not specified
+ port = 443
+ if len(parts) == 2:
+ port = int(parts[1])
+
+ cert_path = os.path.join(CERTS_DIR, url.replace("/", "_").strip())
+
while True:
hashval = ""
event_type, event_ts, message_json = message_queue.get()
@@ -226,26 +249,55 @@ def publish_to_webhook(url, token, secret, message_queue):
if hashval:
http_headers["Authorization"] = "Bearer " + hashval
- try:
- resp = requests.post(url, headers=http_headers, data=message_json)
- except requests.ConnectionError as e:
- logger.warn("Event push failed to URL: {url}, "
- "Event: {event}, "
- "Status: {error}".format(
- url=url,
- event=message_json,
- error=e))
- continue
- finally:
- message_queue.task_done()
-
- if resp.status_code != 200:
- logger.warn("Event push failed to URL: {url}, "
- "Event: {event}, "
- "Status Code: {status_code}".format(
- url=url,
- event=message_json,
- status_code=resp.status_code))
+ verify = True
+ while True:
+ try:
+ resp = requests.post(url, headers=http_headers,
+ data=message_json,
+ verify=verify)
+ # Successful webhook push
+ message_queue.task_done()
+ if resp.status_code != 200:
+ logger.warn("Event push failed to URL: {url}, "
+ "Event: {event}, "
+ "Status Code: {status_code}".format(
+ url=url,
+ event=message_json,
+ status_code=resp.status_code))
+ break
+ except requests.exceptions.SSLError as e:
+ # If verify is equal to cert path, but still failed with
+ # SSLError, Looks like some issue with custom downloaded
+ # certificate, Try with verify = false
+ if verify == cert_path:
+ logger.warn("Event push failed with certificate, "
+ "ignoring verification url={0} "
+ "Error={1}".format(url, e))
+ verify = False
+ continue
+
+ # If verify is instance of bool and True, then custom cert
+ # is required, download the cert and retry
+ try:
+ save_https_cert(domain, port, cert_path)
+ verify = cert_path
+ except Exception as ex:
+ verify = False
+ logger.warn("Unable to get Server certificate, "
+ "ignoring verification url={0} "
+ "Error={1}".format(url, ex))
+
+ # Done with collecting cert, continue
+ continue
+ except Exception as e:
+ logger.warn("Event push failed to URL: {url}, "
+ "Event: {event}, "
+ "Status: {error}".format(
+ url=url,
+ event=message_json,
+ error=e))
+ message_queue.task_done()
+ break
def plugin_webhook(message):
--
1.8.3.1