3f51ca
From 0e1b5a65ed06b2213deebb0ea1e5fb8422223426 Mon Sep 17 00:00:00 2001
3f51ca
From: Christian Heimes <cheimes@redhat.com>
3f51ca
Date: Wed, 8 Nov 2017 15:15:30 +0100
3f51ca
Subject: [PATCH] Backup ipa-custodia conf and keys
3f51ca
3f51ca
https://pagure.io/freeipa/issue/7247
3f51ca
3f51ca
Signed-off-by: Christian Heimes <cheimes@redhat.com>
3f51ca
Reviewed-By: Simo Sorce <ssorce@redhat.com>
3f51ca
---
3f51ca
 install/share/custodia.conf.template    |  2 +-
3f51ca
 ipaplatform/base/paths.py               |  1 +
3f51ca
 ipapython/ipautil.py                    | 19 +++++++++++++++++++
3f51ca
 ipaserver/install/custodiainstance.py   | 24 +++++++++++++-----------
3f51ca
 ipaserver/install/ipa_backup.py         |  2 ++
3f51ca
 ipatests/test_ipapython/test_ipautil.py |  7 +++++++
3f51ca
 6 files changed, 43 insertions(+), 12 deletions(-)
3f51ca
3f51ca
diff --git a/install/share/custodia.conf.template b/install/share/custodia.conf.template
3f51ca
index 855a1b3ba206e4ded8de80758b02473040096c7f..ee3c43ca7ec265aa09d250426bf4138bcfdf62b6 100644
3f51ca
--- a/install/share/custodia.conf.template
3f51ca
+++ b/install/share/custodia.conf.template
3f51ca
@@ -16,7 +16,7 @@ header = GSS_NAME
3f51ca
 handler = ipaserver.secrets.kem.IPAKEMKeys
3f51ca
 paths = /keys
3f51ca
 store = ipa
3f51ca
-server_keys = $IPA_CUSTODIA_CONF_DIR/server.keys
3f51ca
+server_keys = $IPA_CUSTODIA_KEYS
3f51ca
 
3f51ca
 [store:ipa]
3f51ca
 handler = ipaserver.secrets.store.IPASecStore
3f51ca
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
3f51ca
index 804fddee60f787e161947bbe4b1914995257ceb4..42240a71066599ca8b36d10a9e5b23625f868977 100644
3f51ca
--- a/ipaplatform/base/paths.py
3f51ca
+++ b/ipaplatform/base/paths.py
3f51ca
@@ -349,6 +349,7 @@ class BasePathNamespace(object):
3f51ca
     NETWORK_MANAGER_CONFIG_DIR = '/etc/NetworkManager/conf.d'
3f51ca
     IPA_CUSTODIA_CONF_DIR = '/etc/ipa/custodia'
3f51ca
     IPA_CUSTODIA_CONF = '/etc/ipa/custodia/custodia.conf'
3f51ca
+    IPA_CUSTODIA_KEYS = '/etc/ipa/custodia/server.keys'
3f51ca
     IPA_CUSTODIA_SOCKET = '/run/httpd/ipa-custodia.sock'
3f51ca
     IPA_CUSTODIA_AUDIT_LOG = '/var/log/ipa-custodia.audit.log'
3f51ca
     IPA_GETKEYTAB = '/usr/sbin/ipa-getkeytab'
3f51ca
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
3f51ca
index cc52af6d9235cfbd597679231f63667b81a200b4..426b32ef05ab00dcbf37b1e58b6390accee33cb1 100644
3f51ca
--- a/ipapython/ipautil.py
3f51ca
+++ b/ipapython/ipautil.py
3f51ca
@@ -307,6 +307,25 @@ def write_tmp_file(txt):
3f51ca
 
3f51ca
     return fd
3f51ca
 
3f51ca
+
3f51ca
+def flush_sync(f):
3f51ca
+    """Flush and fsync file to disk
3f51ca
+
3f51ca
+    :param f: a file object with fileno and name
3f51ca
+    """
3f51ca
+    # flush file buffer to file descriptor
3f51ca
+    f.flush()
3f51ca
+    # flush Kernel buffer to disk
3f51ca
+    os.fsync(f.fileno())
3f51ca
+    # sync metadata in directory
3f51ca
+    dirname = os.path.dirname(os.path.abspath(f.name))
3f51ca
+    dirfd = os.open(dirname, os.O_RDONLY | os.O_DIRECTORY)
3f51ca
+    try:
3f51ca
+        os.fsync(dirfd)
3f51ca
+    finally:
3f51ca
+        os.close(dirfd)
3f51ca
+
3f51ca
+
3f51ca
 def shell_quote(string):
3f51ca
     if isinstance(string, str):
3f51ca
         return "'" + string.replace("'", "'\\''") + "'"
3f51ca
diff --git a/ipaserver/install/custodiainstance.py b/ipaserver/install/custodiainstance.py
3f51ca
index bc3cea7063dff183c85b4f6e8ced7567f691001d..0a90bb3954486b9773e3553e9981d2a8d0d4e44a 100644
3f51ca
--- a/ipaserver/install/custodiainstance.py
3f51ca
+++ b/ipaserver/install/custodiainstance.py
3f51ca
@@ -25,8 +25,7 @@ class CustodiaInstance(SimpleServiceInstance):
3f51ca
     def __init__(self, host_name=None, realm=None):
3f51ca
         super(CustodiaInstance, self).__init__("ipa-custodia")
3f51ca
         self.config_file = paths.IPA_CUSTODIA_CONF
3f51ca
-        self.server_keys = os.path.join(paths.IPA_CUSTODIA_CONF_DIR,
3f51ca
-                                        'server.keys')
3f51ca
+        self.server_keys = paths.IPA_CUSTODIA_KEYS
3f51ca
         self.ldap_uri = None
3f51ca
         self.fqdn = host_name
3f51ca
         self.realm = realm
3f51ca
@@ -35,16 +34,19 @@ class CustodiaInstance(SimpleServiceInstance):
3f51ca
         template_file = os.path.basename(self.config_file) + '.template'
3f51ca
         template = os.path.join(paths.USR_SHARE_IPA_DIR, template_file)
3f51ca
         httpd_info = pwd.getpwnam(constants.HTTPD_USER)
3f51ca
-        sub_dict = dict(IPA_CUSTODIA_CONF_DIR=paths.IPA_CUSTODIA_CONF_DIR,
3f51ca
-                        IPA_CUSTODIA_SOCKET=paths.IPA_CUSTODIA_SOCKET,
3f51ca
-                        IPA_CUSTODIA_AUDIT_LOG=paths.IPA_CUSTODIA_AUDIT_LOG,
3f51ca
-                        LDAP_URI=installutils.realm_to_ldapi_uri(self.realm),
3f51ca
-                        UID=httpd_info.pw_uid, GID=httpd_info.pw_gid)
3f51ca
+        sub_dict = dict(
3f51ca
+            IPA_CUSTODIA_CONF_DIR=paths.IPA_CUSTODIA_CONF_DIR,
3f51ca
+            IPA_CUSTODIA_KEYS=paths.IPA_CUSTODIA_KEYS,
3f51ca
+            IPA_CUSTODIA_SOCKET=paths.IPA_CUSTODIA_SOCKET,
3f51ca
+            IPA_CUSTODIA_AUDIT_LOG=paths.IPA_CUSTODIA_AUDIT_LOG,
3f51ca
+            LDAP_URI=installutils.realm_to_ldapi_uri(self.realm),
3f51ca
+            UID=httpd_info.pw_uid,
3f51ca
+            GID=httpd_info.pw_gid
3f51ca
+        )
3f51ca
         conf = ipautil.template_file(template, sub_dict)
3f51ca
-        fd = open(self.config_file, "w+")
3f51ca
-        fd.write(conf)
3f51ca
-        fd.flush()
3f51ca
-        fd.close()
3f51ca
+        with open(self.config_file, "w") as f:
3f51ca
+            f.write(conf)
3f51ca
+            ipautil.flush_sync(f)
3f51ca
 
3f51ca
     def create_instance(self):
3f51ca
         suffix = ipautil.realm_to_suffix(self.realm)
3f51ca
diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py
3f51ca
index f8cdd56d26636678279ba5afb423c5eef10c33d0..93b154330d3e6c8700c98860eb0c08f6841774bb 100644
3f51ca
--- a/ipaserver/install/ipa_backup.py
3f51ca
+++ b/ipaserver/install/ipa_backup.py
3f51ca
@@ -181,6 +181,8 @@ class Backup(admintool.AdminTool):
3f51ca
         paths.DNSSEC_SOFTHSM_PIN_SO,
3f51ca
         paths.IPA_ODS_EXPORTER_KEYTAB,
3f51ca
         paths.IPA_DNSKEYSYNCD_KEYTAB,
3f51ca
+        paths.IPA_CUSTODIA_KEYS,
3f51ca
+        paths.IPA_CUSTODIA_CONF,
3f51ca
         paths.HOSTS,
3f51ca
     ) + tuple(
3f51ca
         os.path.join(paths.IPA_NSSDB_DIR, file)
3f51ca
diff --git a/ipatests/test_ipapython/test_ipautil.py b/ipatests/test_ipapython/test_ipautil.py
3f51ca
index 9c351bd0ed9cd96488ac74deadf97996668a75d2..5e1f58003e9f3cae2f0819ecc348ade2c367548b 100644
3f51ca
--- a/ipatests/test_ipapython/test_ipautil.py
3f51ca
+++ b/ipatests/test_ipapython/test_ipautil.py
3f51ca
@@ -25,6 +25,7 @@ Test the `ipapython/ipautil.py` module.
3f51ca
 import nose
3f51ca
 import pytest
3f51ca
 import six
3f51ca
+import tempfile
3f51ca
 
3f51ca
 from ipapython import ipautil
3f51ca
 
3f51ca
@@ -478,3 +479,9 @@ def test_backcompat():
3f51ca
     assert rc is result.returncode
3f51ca
     assert out is result.output
3f51ca
     assert err is result.error_output
3f51ca
+
3f51ca
+
3f51ca
+def test_flush_sync():
3f51ca
+    with tempfile.NamedTemporaryFile('wb+') as f:
3f51ca
+        f.write(b'data')
3f51ca
+        ipautil.flush_sync(f)
3f51ca
-- 
3f51ca
2.13.6
3f51ca