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