mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Prepare migration of mod_nss NSSDB to sql format
- Refactor CertDB to look up values from its NSSDatabase. - Add run_modutil() helpers to support sql format. modutil does not auto-detect the NSSDB format. - Add migration helpers to CertDB. - Add explicit DB format to NSSCertificateDatabase stanza - Restore SELinux context when migrating NSSDB. - Add some debugging and sanity checks to httpinstance. The actual database format is still dbm. Certmonger on Fedora 27 does neither auto-detect DB format nor support SQL out of the box. https://pagure.io/freeipa/issue/7354 Signed-off-by: Christian Heimes <cheimes@redhat.com> Reviewed-By: Stanislav Laznicka <slaznick@redhat.com>
This commit is contained in:
@@ -43,6 +43,7 @@ steps:
|
||||
systemd_journal.log
|
||||
`find daemons -name '*.log' -print`
|
||||
- chown ${uid}:${gid} ${container_working_dir}/var_log.tar
|
||||
- ls -laZ /etc/dirsrv/slapd-*/ /etc/httpd/alias/ /etc/pki/pki-tomcat/alias/ || true
|
||||
configure:
|
||||
- ./autogen.sh
|
||||
install_packages:
|
||||
|
||||
@@ -34,6 +34,7 @@ import cryptography.x509
|
||||
|
||||
from ipaplatform.constants import constants
|
||||
from ipaplatform.paths import paths
|
||||
from ipaplatform.tasks import tasks
|
||||
from ipapython.dn import DN
|
||||
from ipapython.kerberos import Principal
|
||||
from ipapython import ipautil
|
||||
@@ -239,19 +240,28 @@ class NSSDatabase(object):
|
||||
self.pwd_file = os.path.join(self.secdir, 'pwdfile.txt')
|
||||
self.dbtype = None
|
||||
self.certdb = self.keydb = self.secmod = None
|
||||
# files in actual db
|
||||
self.filenames = ()
|
||||
# all files that are handled by create_db(backup=True)
|
||||
self.backup_filenames = ()
|
||||
self._set_filenames(dbtype)
|
||||
|
||||
def _set_filenames(self, dbtype):
|
||||
self.dbtype = dbtype
|
||||
dbmfiles = (
|
||||
os.path.join(self.secdir, "cert8.db"),
|
||||
os.path.join(self.secdir, "key3.db"),
|
||||
os.path.join(self.secdir, "secmod.db")
|
||||
)
|
||||
sqlfiles = (
|
||||
os.path.join(self.secdir, "cert9.db"),
|
||||
os.path.join(self.secdir, "key4.db"),
|
||||
os.path.join(self.secdir, "pkcs11.txt")
|
||||
)
|
||||
if dbtype == 'dbm':
|
||||
self.certdb = os.path.join(self.secdir, "cert8.db")
|
||||
self.keydb = os.path.join(self.secdir, "key3.db")
|
||||
self.secmod = os.path.join(self.secdir, "secmod.db")
|
||||
self.certdb, self.keydb, self.secmod = dbmfiles
|
||||
elif dbtype == 'sql':
|
||||
self.certdb = os.path.join(self.secdir, "cert9.db")
|
||||
self.keydb = os.path.join(self.secdir, "key4.db")
|
||||
self.secmod = os.path.join(self.secdir, "pkcs11.txt")
|
||||
self.certdb, self.keydb, self.secmod = sqlfiles
|
||||
else:
|
||||
raise ValueError(dbtype)
|
||||
self.filenames = (
|
||||
@@ -260,6 +270,9 @@ class NSSDatabase(object):
|
||||
self.secmod,
|
||||
self.pwd_file,
|
||||
)
|
||||
self.backup_filenames = (
|
||||
self.pwd_file,
|
||||
) + sqlfiles + dbmfiles
|
||||
|
||||
def close(self):
|
||||
if self._is_temporary:
|
||||
@@ -288,6 +301,19 @@ class NSSDatabase(object):
|
||||
new_args.extend(args)
|
||||
return ipautil.run(new_args, stdin, **kwargs)
|
||||
|
||||
def run_modutil(self, args, stdin=None, **kwargs):
|
||||
new_args = [
|
||||
paths.MODUTIL,
|
||||
'-dbdir', '{}:{}'.format(self.dbtype, self.secdir)
|
||||
]
|
||||
new_args.extend(args)
|
||||
return ipautil.run(new_args, stdin, **kwargs)
|
||||
|
||||
def exists(self):
|
||||
"""Check DB exists (all files are present)
|
||||
"""
|
||||
return all(os.path.isfile(filename) for filename in self.filenames)
|
||||
|
||||
def create_db(self, user=None, group=None, mode=None, backup=False):
|
||||
"""Create cert DB
|
||||
|
||||
@@ -313,9 +339,8 @@ class NSSDatabase(object):
|
||||
gid = grp.getgrnam(group).gr_gid
|
||||
|
||||
if backup:
|
||||
for filename in self.filenames:
|
||||
path = os.path.join(self.secdir, filename)
|
||||
ipautil.backup_file(path)
|
||||
for filename in self.backup_filenames:
|
||||
ipautil.backup_file(filename)
|
||||
|
||||
if not os.path.exists(self.secdir):
|
||||
os.makedirs(self.secdir, dirmode)
|
||||
@@ -328,7 +353,8 @@ class NSSDatabase(object):
|
||||
f.write(ipautil.ipa_generate_password())
|
||||
f.flush()
|
||||
|
||||
self.run_certutil(["-N", "-f", self.pwd_file])
|
||||
# -@ in case it's an old db and it must be migrated
|
||||
self.run_certutil(['-N', '-@', self.pwd_file])
|
||||
|
||||
# Finally fix up perms
|
||||
os.chown(self.secdir, uid, gid)
|
||||
@@ -383,7 +409,7 @@ class NSSDatabase(object):
|
||||
oldstat = os.stat(oldname)
|
||||
os.chmod(newname, stat.S_IMODE(oldstat.st_mode))
|
||||
os.chown(newname, oldstat.st_uid, oldstat.st_gid)
|
||||
# XXX also retain SELinux context?
|
||||
tasks.restore_context(newname)
|
||||
|
||||
self._set_filenames('sql')
|
||||
self.list_certs() # self-test
|
||||
|
||||
@@ -45,8 +45,10 @@ from ipalib.errors import CertificateOperationError
|
||||
from ipalib.install import certstore
|
||||
from ipalib.util import strip_csr_header
|
||||
from ipalib.text import _
|
||||
from ipaplatform.constants import constants
|
||||
from ipaplatform.paths import paths
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -147,14 +149,10 @@ class CertDB(object):
|
||||
dbtype='auto'):
|
||||
self.nssdb = NSSDatabase(nssdir, dbtype=dbtype)
|
||||
|
||||
self.secdir = nssdir
|
||||
self.realm = realm
|
||||
|
||||
self.noise_fname = os.path.join(self.secdir, "noise.txt")
|
||||
|
||||
self.certdb_fname = self.nssdb.certdb
|
||||
self.keydb_fname = self.nssdb.keydb
|
||||
self.secmod_fname = self.nssdb.secmod
|
||||
self.pk12_fname = os.path.join(self.secdir, "cacert.p12")
|
||||
self.pin_fname = os.path.join(self.secdir + "pin.txt")
|
||||
self.reqdir = None
|
||||
@@ -205,6 +203,27 @@ class CertDB(object):
|
||||
ca_subject = ipautil.dn_attribute_property('_ca_subject')
|
||||
subject_base = ipautil.dn_attribute_property('_subject_base')
|
||||
|
||||
# migration changes paths, just forward attribute lookup to nssdb
|
||||
@property
|
||||
def secdir(self):
|
||||
return self.nssdb.secdir
|
||||
|
||||
@property
|
||||
def dbtype(self):
|
||||
return self.nssdb.dbtype
|
||||
|
||||
@property
|
||||
def certdb_fname(self):
|
||||
return self.nssdb.certdb
|
||||
|
||||
@property
|
||||
def keydb_fname(self):
|
||||
return self.nssdb.keydb
|
||||
|
||||
@property
|
||||
def secmod_fname(self):
|
||||
return self.nssdb.secmod
|
||||
|
||||
@property
|
||||
def passwd_fname(self):
|
||||
return self.nssdb.pwd_file
|
||||
@@ -213,10 +232,7 @@ class CertDB(object):
|
||||
"""
|
||||
Checks whether all NSS database files + our pwd_file exist
|
||||
"""
|
||||
for f in self.nssdb.filenames:
|
||||
if not os.path.exists(f):
|
||||
return False
|
||||
return True
|
||||
return self.nssdb.exists()
|
||||
|
||||
def __del__(self):
|
||||
if self.reqdir is not None:
|
||||
@@ -261,6 +277,9 @@ class CertDB(object):
|
||||
def run_certutil(self, args, stdin=None, **kwargs):
|
||||
return self.nssdb.run_certutil(args, stdin, **kwargs)
|
||||
|
||||
def run_modutil(self, args, stdin=None, **kwargs):
|
||||
return self.nssdb.run_modutil(args, stdin, **kwargs)
|
||||
|
||||
def create_noise_file(self):
|
||||
if os.path.isfile(self.noise_fname):
|
||||
os.remove(self.noise_fname)
|
||||
@@ -278,8 +297,10 @@ class CertDB(object):
|
||||
f.write(ipautil.ipa_generate_password())
|
||||
|
||||
def create_certdbs(self):
|
||||
self.nssdb.create_db(user=self.user, group=self.group, mode=self.mode,
|
||||
backup=True)
|
||||
self.nssdb.create_db(
|
||||
user=self.user, group=self.group, mode=self.mode,
|
||||
backup=True
|
||||
)
|
||||
self.set_perms(self.passwd_fname, write=True)
|
||||
|
||||
def restore(self):
|
||||
@@ -670,6 +691,49 @@ class CertDB(object):
|
||||
|
||||
return is_ipa_issued_cert(api, cert)
|
||||
|
||||
def disable_system_trust(self):
|
||||
"""Disable system trust module of NSSDB
|
||||
"""
|
||||
name = 'Root Certs'
|
||||
try:
|
||||
result = self.run_modutil(
|
||||
['-force', '-list', name],
|
||||
env={},
|
||||
capture_output=True
|
||||
)
|
||||
except ipautil.CalledProcessError as e:
|
||||
if e.returncode == 29: # ERROR: Module not found in database.
|
||||
logger.debug(
|
||||
'Module %s not available, treating as disabled', name)
|
||||
return False
|
||||
raise
|
||||
|
||||
if 'Status: Enabled' in result.output:
|
||||
self.run_modutil(
|
||||
['-force', '-disable', name],
|
||||
env={}
|
||||
)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def needs_upgrade_format(self):
|
||||
"""Check if NSSDB file format needs upgrade
|
||||
|
||||
Only upgrade if it's an existing dbm database and default
|
||||
database type is no 'dbm'.
|
||||
"""
|
||||
return (
|
||||
self.nssdb.dbtype == 'dbm' and
|
||||
self.nssdb.dbtype != constants.NSS_DEFAULT_DBTYPE and
|
||||
self.exists()
|
||||
)
|
||||
|
||||
def upgrade_format(self):
|
||||
"""Upgrade NSSDB to new file format
|
||||
"""
|
||||
self.nssdb.convert_db()
|
||||
|
||||
|
||||
class _CrossProcessLock(object):
|
||||
_DATETIME_FORMAT = '%Y%m%d%H%M%S%f'
|
||||
|
||||
@@ -361,7 +361,8 @@ class HTTPInstance(service.Service):
|
||||
ca='IPA',
|
||||
profile=dogtag.DEFAULT_PROFILE,
|
||||
dns=[self.fqdn],
|
||||
post_command='restart_httpd'
|
||||
post_command='restart_httpd',
|
||||
storage='FILE',
|
||||
)
|
||||
finally:
|
||||
if prev_helper is not None:
|
||||
|
||||
@@ -369,6 +369,8 @@ class Service(object):
|
||||
|
||||
This server cert should be in DER format.
|
||||
"""
|
||||
if self.cert is None:
|
||||
raise ValueError("{} has no cert".format(self.service_name))
|
||||
dn = DN(('krbprincipalname', self.principal), ('cn', 'services'),
|
||||
('cn', 'accounts'), self.suffix)
|
||||
entry = api.Backend.ldap2.get_entry(dn)
|
||||
|
||||
@@ -30,11 +30,13 @@ def test_dbm_tmp():
|
||||
|
||||
for filename in nssdb.filenames:
|
||||
assert not os.path.isfile(filename)
|
||||
assert not nssdb.exists()
|
||||
|
||||
nssdb.create_db()
|
||||
for filename in nssdb.filenames:
|
||||
assert os.path.isfile(filename)
|
||||
assert os.path.dirname(filename) == nssdb.secdir
|
||||
assert nssdb.exists()
|
||||
|
||||
assert os.path.basename(nssdb.certdb) == 'cert8.db'
|
||||
assert nssdb.certdb in nssdb.filenames
|
||||
@@ -48,11 +50,13 @@ def test_sql_tmp():
|
||||
|
||||
for filename in nssdb.filenames:
|
||||
assert not os.path.isfile(filename)
|
||||
assert not nssdb.exists()
|
||||
|
||||
nssdb.create_db()
|
||||
for filename in nssdb.filenames:
|
||||
assert os.path.isfile(filename)
|
||||
assert os.path.dirname(filename) == nssdb.secdir
|
||||
assert nssdb.exists()
|
||||
|
||||
assert os.path.basename(nssdb.certdb) == 'cert9.db'
|
||||
assert nssdb.certdb in nssdb.filenames
|
||||
@@ -65,6 +69,7 @@ def test_convert_db():
|
||||
assert nssdb.dbtype == 'dbm'
|
||||
|
||||
nssdb.create_db()
|
||||
assert nssdb.exists()
|
||||
|
||||
create_selfsigned(nssdb)
|
||||
|
||||
@@ -74,6 +79,7 @@ def test_convert_db():
|
||||
assert len(oldkeys) == 1
|
||||
|
||||
nssdb.convert_db()
|
||||
assert nssdb.exists()
|
||||
|
||||
assert nssdb.dbtype == 'sql'
|
||||
newcerts = nssdb.list_certs()
|
||||
|
||||
Reference in New Issue
Block a user