|
|
ac7d03 |
From 1190a1b41d436de4dab7a622d78217baba44a9ef Mon Sep 17 00:00:00 2001
|
|
|
ac7d03 |
From: Christian Heimes <cheimes@redhat.com>
|
|
|
ac7d03 |
Date: Fri, 17 Mar 2017 10:44:38 +0100
|
|
|
ac7d03 |
Subject: [PATCH] Simplify KRA transport cert cache
|
|
|
ac7d03 |
|
|
|
ac7d03 |
In-memory cache causes problem in forking servers. A file based cache is
|
|
|
ac7d03 |
good enough. It's easier to understand and avoids performance regression
|
|
|
ac7d03 |
and synchronization issues when cert becomes out-of-date.
|
|
|
ac7d03 |
|
|
|
ac7d03 |
https://pagure.io/freeipa/issue/6787
|
|
|
ac7d03 |
Signed-off-by: Christian Heimes <cheimes@redhat.com>
|
|
|
ac7d03 |
Reviewed-By: Jan Cholasta <jcholast@redhat.com>
|
|
|
ac7d03 |
---
|
|
|
ac7d03 |
ipaclient/plugins/vault.py | 103 ++++++++++++++++++++++++---------------------
|
|
|
ac7d03 |
1 file changed, 55 insertions(+), 48 deletions(-)
|
|
|
ac7d03 |
|
|
|
ac7d03 |
diff --git a/ipaclient/plugins/vault.py b/ipaclient/plugins/vault.py
|
|
|
ac7d03 |
index d677ec0287d6b37cfd63820a919c0726d3a4ae9f..3fb4900d9cf90e6902c40e1c3d8cfdafec2e28b8 100644
|
|
|
ac7d03 |
--- a/ipaclient/plugins/vault.py
|
|
|
ac7d03 |
+++ b/ipaclient/plugins/vault.py
|
|
|
ac7d03 |
@@ -20,7 +20,6 @@
|
|
|
ac7d03 |
from __future__ import print_function
|
|
|
ac7d03 |
|
|
|
ac7d03 |
import base64
|
|
|
ac7d03 |
-import collections
|
|
|
ac7d03 |
import errno
|
|
|
ac7d03 |
import getpass
|
|
|
ac7d03 |
import io
|
|
|
ac7d03 |
@@ -558,74 +557,79 @@ class vault_mod(Local):
|
|
|
ac7d03 |
return response
|
|
|
ac7d03 |
|
|
|
ac7d03 |
|
|
|
ac7d03 |
-class _TransportCertCache(collections.MutableMapping):
|
|
|
ac7d03 |
+class _TransportCertCache(object):
|
|
|
ac7d03 |
def __init__(self):
|
|
|
ac7d03 |
self._dirname = os.path.join(
|
|
|
ac7d03 |
- USER_CACHE_PATH, 'ipa', 'kra-transport-certs')
|
|
|
ac7d03 |
- self._transport_certs = {}
|
|
|
ac7d03 |
+ USER_CACHE_PATH, 'ipa', 'kra-transport-certs'
|
|
|
ac7d03 |
+ )
|
|
|
ac7d03 |
|
|
|
ac7d03 |
def _get_filename(self, domain):
|
|
|
ac7d03 |
basename = DNSName(domain).ToASCII() + '.pem'
|
|
|
ac7d03 |
return os.path.join(self._dirname, basename)
|
|
|
ac7d03 |
|
|
|
ac7d03 |
- def __getitem__(self, domain):
|
|
|
ac7d03 |
- try:
|
|
|
ac7d03 |
- transport_cert = self._transport_certs[domain]
|
|
|
ac7d03 |
- except KeyError:
|
|
|
ac7d03 |
- transport_cert = None
|
|
|
ac7d03 |
+ def load_cert(self, domain):
|
|
|
ac7d03 |
+ """Load cert from cache
|
|
|
ac7d03 |
|
|
|
ac7d03 |
- filename = self._get_filename(domain)
|
|
|
ac7d03 |
+ :param domain: IPA domain
|
|
|
ac7d03 |
+ :return: cryptography.x509.Certificate or None
|
|
|
ac7d03 |
+ """
|
|
|
ac7d03 |
+ filename = self._get_filename(domain)
|
|
|
ac7d03 |
+ try:
|
|
|
ac7d03 |
try:
|
|
|
ac7d03 |
- try:
|
|
|
ac7d03 |
- transport_cert = x509.load_certificate_from_file(filename)
|
|
|
ac7d03 |
- except EnvironmentError as e:
|
|
|
ac7d03 |
- if e.errno != errno.ENOENT:
|
|
|
ac7d03 |
- raise
|
|
|
ac7d03 |
- except Exception:
|
|
|
ac7d03 |
- logger.warning("Failed to load %s: %s", filename,
|
|
|
ac7d03 |
- exc_info=True)
|
|
|
ac7d03 |
-
|
|
|
ac7d03 |
- if transport_cert is None:
|
|
|
ac7d03 |
- raise KeyError(domain)
|
|
|
ac7d03 |
-
|
|
|
ac7d03 |
- self._transport_certs[domain] = transport_cert
|
|
|
ac7d03 |
+ return x509.load_certificate_from_file(filename)
|
|
|
ac7d03 |
+ except EnvironmentError as e:
|
|
|
ac7d03 |
+ if e.errno != errno.ENOENT:
|
|
|
ac7d03 |
+ raise
|
|
|
ac7d03 |
+ except Exception:
|
|
|
ac7d03 |
+ logger.warning("Failed to load %s", filename, exc_info=True)
|
|
|
ac7d03 |
|
|
|
ac7d03 |
- return transport_cert
|
|
|
ac7d03 |
+ def store_cert(self, domain, transport_cert):
|
|
|
ac7d03 |
+ """Store a new cert or override existing cert
|
|
|
ac7d03 |
|
|
|
ac7d03 |
- def __setitem__(self, domain, transport_cert):
|
|
|
ac7d03 |
+ :param domain: IPA domain
|
|
|
ac7d03 |
+ :param transport_cert: cryptography.x509.Certificate
|
|
|
ac7d03 |
+ :return: True if cert was stored successfully
|
|
|
ac7d03 |
+ """
|
|
|
ac7d03 |
filename = self._get_filename(domain)
|
|
|
ac7d03 |
- transport_cert_der = (
|
|
|
ac7d03 |
- transport_cert.public_bytes(serialization.Encoding.DER))
|
|
|
ac7d03 |
+ pem = transport_cert.public_bytes(serialization.Encoding.PEM)
|
|
|
ac7d03 |
try:
|
|
|
ac7d03 |
try:
|
|
|
ac7d03 |
os.makedirs(self._dirname)
|
|
|
ac7d03 |
except EnvironmentError as e:
|
|
|
ac7d03 |
if e.errno != errno.EEXIST:
|
|
|
ac7d03 |
raise
|
|
|
ac7d03 |
- fd, tmpfilename = tempfile.mkstemp(dir=self._dirname)
|
|
|
ac7d03 |
- os.close(fd)
|
|
|
ac7d03 |
- x509.write_certificate(transport_cert_der, tmpfilename)
|
|
|
ac7d03 |
- os.rename(tmpfilename, filename)
|
|
|
ac7d03 |
+ with tempfile.NamedTemporaryFile(dir=self._dirname, delete=False,
|
|
|
ac7d03 |
+ mode='wb') as f:
|
|
|
ac7d03 |
+ try:
|
|
|
ac7d03 |
+ f.write(pem)
|
|
|
ac7d03 |
+ f.flush()
|
|
|
ac7d03 |
+ os.fdatasync(f.fileno())
|
|
|
ac7d03 |
+ f.close()
|
|
|
ac7d03 |
+ os.rename(f.name, filename)
|
|
|
ac7d03 |
+ except Exception:
|
|
|
ac7d03 |
+ os.unlink(f.name)
|
|
|
ac7d03 |
+ raise
|
|
|
ac7d03 |
except Exception:
|
|
|
ac7d03 |
logger.warning("Failed to save %s", filename, exc_info=True)
|
|
|
ac7d03 |
+ return False
|
|
|
ac7d03 |
+ else:
|
|
|
ac7d03 |
+ return True
|
|
|
ac7d03 |
|
|
|
ac7d03 |
- self._transport_certs[domain] = transport_cert
|
|
|
ac7d03 |
+ def remove_cert(self, domain):
|
|
|
ac7d03 |
+ """Remove a cert from cache, ignores errors
|
|
|
ac7d03 |
|
|
|
ac7d03 |
- def __delitem__(self, domain):
|
|
|
ac7d03 |
+ :param domain: IPA domain
|
|
|
ac7d03 |
+ :return: True if cert was found and removed
|
|
|
ac7d03 |
+ """
|
|
|
ac7d03 |
filename = self._get_filename(domain)
|
|
|
ac7d03 |
try:
|
|
|
ac7d03 |
os.unlink(filename)
|
|
|
ac7d03 |
except EnvironmentError as e:
|
|
|
ac7d03 |
if e.errno != errno.ENOENT:
|
|
|
ac7d03 |
logger.warning("Failed to remove %s", filename, exc_info=True)
|
|
|
ac7d03 |
-
|
|
|
ac7d03 |
- del self._transport_certs[domain]
|
|
|
ac7d03 |
-
|
|
|
ac7d03 |
- def __len__(self):
|
|
|
ac7d03 |
- return len(self._transport_certs)
|
|
|
ac7d03 |
-
|
|
|
ac7d03 |
- def __iter__(self):
|
|
|
ac7d03 |
- return iter(self._transport_certs)
|
|
|
ac7d03 |
+ return False
|
|
|
ac7d03 |
+ else:
|
|
|
ac7d03 |
+ return True
|
|
|
ac7d03 |
|
|
|
ac7d03 |
|
|
|
ac7d03 |
_transport_cert_cache = _TransportCertCache()
|
|
|
ac7d03 |
@@ -646,7 +650,10 @@ class vaultconfig_show(MethodOverride):
|
|
|
ac7d03 |
# cache transport certificate
|
|
|
ac7d03 |
transport_cert = x509.load_certificate(
|
|
|
ac7d03 |
response['result']['transport_cert'], x509.DER)
|
|
|
ac7d03 |
- _transport_cert_cache[self.api.env.domain] = transport_cert
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+ _transport_cert_cache.store_cert(
|
|
|
ac7d03 |
+ self.api.env.domain, transport_cert
|
|
|
ac7d03 |
+ )
|
|
|
ac7d03 |
|
|
|
ac7d03 |
if file:
|
|
|
ac7d03 |
with open(file, 'w') as f:
|
|
|
ac7d03 |
@@ -680,7 +687,7 @@ class ModVaultData(Local):
|
|
|
ac7d03 |
except (errors.InternalError,
|
|
|
ac7d03 |
errors.ExecutionError,
|
|
|
ac7d03 |
errors.GenericError):
|
|
|
ac7d03 |
- _transport_cert_cache.pop(self.api.env.domain, None)
|
|
|
ac7d03 |
+ _transport_cert_cache.remove_cert(self.api.env.domain)
|
|
|
ac7d03 |
if raise_unexpected:
|
|
|
ac7d03 |
raise
|
|
|
ac7d03 |
|
|
|
ac7d03 |
@@ -691,17 +698,17 @@ class ModVaultData(Local):
|
|
|
ac7d03 |
domain = self.api.env.domain
|
|
|
ac7d03 |
|
|
|
ac7d03 |
# try call with cached transport certificate
|
|
|
ac7d03 |
- transport_cert = _transport_cert_cache.get(domain)
|
|
|
ac7d03 |
+ transport_cert = _transport_cert_cache.load_cert(domain)
|
|
|
ac7d03 |
if transport_cert is not None:
|
|
|
ac7d03 |
result = self._do_internal(algo, transport_cert, False,
|
|
|
ac7d03 |
*args, **options)
|
|
|
ac7d03 |
if result is not None:
|
|
|
ac7d03 |
return result
|
|
|
ac7d03 |
|
|
|
ac7d03 |
- # retrieve and cache transport certificate
|
|
|
ac7d03 |
- self.api.Command.vaultconfig_show()
|
|
|
ac7d03 |
- transport_cert = _transport_cert_cache[domain]
|
|
|
ac7d03 |
-
|
|
|
ac7d03 |
+ # retrieve transport certificate (cached by vaultconfig_show)
|
|
|
ac7d03 |
+ response = self.api.Command.vaultconfig_show()
|
|
|
ac7d03 |
+ transport_cert = x509.load_certificate(
|
|
|
ac7d03 |
+ response['result']['transport_cert'], x509.DER)
|
|
|
ac7d03 |
# call with the retrieved transport certificate
|
|
|
ac7d03 |
return self._do_internal(algo, transport_cert, True,
|
|
|
ac7d03 |
*args, **options)
|
|
|
ac7d03 |
--
|
|
|
ac7d03 |
2.12.1
|
|
|
ac7d03 |
|