Blob Blame History Raw
From 0e1b5a65ed06b2213deebb0ea1e5fb8422223426 Mon Sep 17 00:00:00 2001
From: Christian Heimes <cheimes@redhat.com>
Date: Wed, 8 Nov 2017 15:15:30 +0100
Subject: [PATCH] Backup ipa-custodia conf and keys

https://pagure.io/freeipa/issue/7247

Signed-off-by: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Simo Sorce <ssorce@redhat.com>
---
 install/share/custodia.conf.template    |  2 +-
 ipaplatform/base/paths.py               |  1 +
 ipapython/ipautil.py                    | 19 +++++++++++++++++++
 ipaserver/install/custodiainstance.py   | 24 +++++++++++++-----------
 ipaserver/install/ipa_backup.py         |  2 ++
 ipatests/test_ipapython/test_ipautil.py |  7 +++++++
 6 files changed, 43 insertions(+), 12 deletions(-)

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