summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordvora-h <67596500+dvora-h@users.noreply.github.com>2022-03-14 14:43:37 +0200
committerGitHub <noreply@github.com>2022-03-14 14:43:37 +0200
commita12b5fd5e9af55e4668c78b50388c4ffb73fdda0 (patch)
treed9210a5b58284f65ca7afd5ad411a6857109315b
parenta081173b650dd73fca06d419156ab525bc1c2200 (diff)
downloadredis-py-a12b5fd5e9af55e4668c78b50388c4ffb73fdda0.tar.gz
Add support to use certificates from string in ssl connection (#2048)
* ssl string cert * fix async test * linters * change test name
-rw-r--r--redis/asyncio/client.py2
-rw-r--r--redis/asyncio/connection.py13
-rwxr-xr-xredis/client.py2
-rw-r--r--redis/cluster.py1
-rwxr-xr-xredis/connection.py13
-rw-r--r--tests/test_ssl.py16
6 files changed, 43 insertions, 4 deletions
diff --git a/redis/asyncio/client.py b/redis/asyncio/client.py
index 2afad0f..bbf2a67 100644
--- a/redis/asyncio/client.py
+++ b/redis/asyncio/client.py
@@ -163,6 +163,7 @@ class Redis(
ssl_certfile: Optional[str] = None,
ssl_cert_reqs: str = "required",
ssl_ca_certs: Optional[str] = None,
+ ssl_ca_data: Optional[str] = None,
ssl_check_hostname: bool = False,
max_connections: Optional[int] = None,
single_connection_client: bool = False,
@@ -228,6 +229,7 @@ class Redis(
"ssl_certfile": ssl_certfile,
"ssl_cert_reqs": ssl_cert_reqs,
"ssl_ca_certs": ssl_ca_certs,
+ "ssl_ca_data": ssl_ca_data,
"ssl_check_hostname": ssl_check_hostname,
}
)
diff --git a/redis/asyncio/connection.py b/redis/asyncio/connection.py
index 77fe95f..0c53985 100644
--- a/redis/asyncio/connection.py
+++ b/redis/asyncio/connection.py
@@ -1026,6 +1026,7 @@ class SSLConnection(Connection):
ssl_certfile: Optional[str] = None,
ssl_cert_reqs: str = "required",
ssl_ca_certs: Optional[str] = None,
+ ssl_ca_data: Optional[str] = None,
ssl_check_hostname: bool = False,
**kwargs,
):
@@ -1035,6 +1036,7 @@ class SSLConnection(Connection):
certfile=ssl_certfile,
cert_reqs=ssl_cert_reqs,
ca_certs=ssl_ca_certs,
+ ca_data=ssl_ca_data,
check_hostname=ssl_check_hostname,
)
@@ -1055,6 +1057,10 @@ class SSLConnection(Connection):
return self.ssl_context.ca_certs
@property
+ def ca_data(self):
+ return self.ssl_context.ca_data
+
+ @property
def check_hostname(self):
return self.ssl_context.check_hostname
@@ -1065,6 +1071,7 @@ class RedisSSLContext:
"certfile",
"cert_reqs",
"ca_certs",
+ "ca_data",
"context",
"check_hostname",
)
@@ -1075,6 +1082,7 @@ class RedisSSLContext:
certfile: Optional[str] = None,
cert_reqs: Optional[str] = None,
ca_certs: Optional[str] = None,
+ ca_data: Optional[str] = None,
check_hostname: bool = False,
):
self.keyfile = keyfile
@@ -1093,6 +1101,7 @@ class RedisSSLContext:
)
self.cert_reqs = CERT_REQS[cert_reqs]
self.ca_certs = ca_certs
+ self.ca_data = ca_data
self.check_hostname = check_hostname
self.context: Optional[ssl.SSLContext] = None
@@ -1103,8 +1112,8 @@ class RedisSSLContext:
context.verify_mode = self.cert_reqs
if self.certfile and self.keyfile:
context.load_cert_chain(certfile=self.certfile, keyfile=self.keyfile)
- if self.ca_certs:
- context.load_verify_locations(self.ca_certs)
+ if self.ca_certs or self.ca_data:
+ context.load_verify_locations(cafile=self.ca_certs, cadata=self.ca_data)
self.context = context
return self.context
diff --git a/redis/client.py b/redis/client.py
index c51ab91..8c17665 100755
--- a/redis/client.py
+++ b/redis/client.py
@@ -885,6 +885,7 @@ class Redis(AbstractRedis, RedisModuleCommands, CoreCommands, SentinelCommands):
ssl_cert_reqs="required",
ssl_ca_certs=None,
ssl_ca_path=None,
+ ssl_ca_data=None,
ssl_check_hostname=False,
ssl_password=None,
ssl_validate_ocsp=False,
@@ -966,6 +967,7 @@ class Redis(AbstractRedis, RedisModuleCommands, CoreCommands, SentinelCommands):
"ssl_certfile": ssl_certfile,
"ssl_cert_reqs": ssl_cert_reqs,
"ssl_ca_certs": ssl_ca_certs,
+ "ssl_ca_data": ssl_ca_data,
"ssl_check_hostname": ssl_check_hostname,
"ssl_password": ssl_password,
"ssl_ca_path": ssl_ca_path,
diff --git a/redis/cluster.py b/redis/cluster.py
index cfec4fc..87643a7 100644
--- a/redis/cluster.py
+++ b/redis/cluster.py
@@ -116,6 +116,7 @@ REDIS_ALLOWED_KEYS = (
"socket_timeout",
"ssl",
"ssl_ca_certs",
+ "ssl_ca_data",
"ssl_certfile",
"ssl_cert_reqs",
"ssl_keyfile",
diff --git a/redis/connection.py b/redis/connection.py
index 8a77e4b..0f97361 100755
--- a/redis/connection.py
+++ b/redis/connection.py
@@ -923,6 +923,7 @@ class SSLConnection(Connection):
ssl_certfile=None,
ssl_cert_reqs="required",
ssl_ca_certs=None,
+ ssl_ca_data=None,
ssl_check_hostname=False,
ssl_ca_path=None,
ssl_password=None,
@@ -939,6 +940,7 @@ class SSLConnection(Connection):
ssl_certfile: Path to an ssl certificate. Defaults to None.
ssl_cert_reqs: The string value for the SSLContext.verify_mode (none, optional, required). Defaults to "required".
ssl_ca_certs: The path to a file of concatenated CA certificates in PEM format. Defaults to None.
+ ssl_ca_data: Either an ASCII string of one or more PEM-encoded certificates or a bytes-like object of DER-encoded certificates.
ssl_check_hostname: If set, match the hostname during the SSL handshake. Defaults to False.
ssl_ca_path: The path to a directory containing several CA certificates in PEM format. Defaults to None.
ssl_password: Password for unlocking an encrypted private key. Defaults to None.
@@ -973,6 +975,7 @@ class SSLConnection(Connection):
ssl_cert_reqs = CERT_REQS[ssl_cert_reqs]
self.cert_reqs = ssl_cert_reqs
self.ca_certs = ssl_ca_certs
+ self.ca_data = ssl_ca_data
self.ca_path = ssl_ca_path
self.check_hostname = ssl_check_hostname
self.certificate_password = ssl_password
@@ -993,8 +996,14 @@ class SSLConnection(Connection):
keyfile=self.keyfile,
password=self.certificate_password,
)
- if self.ca_certs is not None or self.ca_path is not None:
- context.load_verify_locations(cafile=self.ca_certs, capath=self.ca_path)
+ if (
+ self.ca_certs is not None
+ or self.ca_path is not None
+ or self.ca_data is not None
+ ):
+ context.load_verify_locations(
+ cafile=self.ca_certs, capath=self.ca_path, cadata=self.ca_data
+ )
sslsock = context.wrap_socket(sock, server_hostname=self.host)
if self.ssl_validate_ocsp is True and CRYPTOGRAPHY_AVAILABLE is False:
raise RedisError("cryptography is not installed.")
diff --git a/tests/test_ssl.py b/tests/test_ssl.py
index ab5d47f..d029b80 100644
--- a/tests/test_ssl.py
+++ b/tests/test_ssl.py
@@ -67,6 +67,22 @@ class TestSSL:
)
assert r.ping()
+ def test_validating_self_signed_string_certificate(self, request):
+ f = open(self.SERVER_CERT)
+ cert_data = f.read()
+ ssl_url = request.config.option.redis_ssl_url
+ p = urlparse(ssl_url)[1].split(":")
+ r = redis.Redis(
+ host=p[0],
+ port=p[1],
+ ssl=True,
+ ssl_certfile=self.SERVER_CERT,
+ ssl_keyfile=self.SERVER_KEY,
+ ssl_cert_reqs="required",
+ ssl_ca_data=cert_data,
+ )
+ assert r.ping()
+
def _create_oscp_conn(self, request):
ssl_url = request.config.option.redis_ssl_url
p = urlparse(ssl_url)[1].split(":")