summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Basti <mbasti@redhat.com>2015-05-22 17:38:16 +0200
committerJan Cholasta <jcholast@redhat.com>2015-05-25 16:34:44 +0000
commit027515230a93a7a60983d3eca26a97a0d9c3610e (patch)
treecf3e0356373d5dde1711a64e47035c06aaaf12fd
parent6a4b428120c2e351ad0f1b4573f50b106844b1fd (diff)
downloadfreeipa-027515230a93a7a60983d3eca26a97a0d9c3610e.tar.gz
freeipa-027515230a93a7a60983d3eca26a97a0d9c3610e.tar.xz
freeipa-027515230a93a7a60983d3eca26a97a0d9c3610e.zip
Server Upgrade: Move code from ipa-upgradeconfig to separate module
This also prevent the script ipa-upgradeconfig execute upgrading. Upgrade of services is called from ipa-server-upgrade https://fedorahosted.org/freeipa/ticket/4904 Reviewed-By: Jan Cholasta <jcholast@redhat.com>
-rwxr-xr-xinstall/tools/ipa-upgradeconfig1412
-rw-r--r--ipaserver/install/ipa_server_upgrade.py15
-rw-r--r--ipaserver/install/server.py1376
3 files changed, 1383 insertions, 1420 deletions
diff --git a/install/tools/ipa-upgradeconfig b/install/tools/ipa-upgradeconfig
index dfef1e0aa..43292966a 100755
--- a/install/tools/ipa-upgradeconfig
+++ b/install/tools/ipa-upgradeconfig
@@ -19,1417 +19,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""
-Upgrade configuration files to a newer template.
-"""
-
import sys
-import re
-import os
-import shutil
-import pwd
-import fileinput
-import ConfigParser
-import grp
-
-from ipalib import api
-import SSSDConfig
-import ipalib.util
-import ipalib.errors
-from ipaplatform import services
-from ipaplatform.tasks import tasks
-from ipapython import ipautil, sysrestore, version, certdb
-from ipapython.config import IPAOptionParser
-from ipapython.ipa_log_manager import *
-from ipapython import certmonger
-from ipapython import dogtag
-from ipaplatform.paths import paths
-from ipaserver.install import installutils
-from ipaserver.install import dsinstance
-from ipaserver.install import httpinstance
-from ipaserver.install import memcacheinstance
-from ipaserver.install import bindinstance
-from ipaserver.install import service
-from ipaserver.install import cainstance
-from ipaserver.install import certs
-from ipaserver.install import otpdinstance
-from ipaserver.install import sysupgrade
-from ipaserver.install import dnskeysyncinstance
-
-
-def parse_options():
- parser = IPAOptionParser(version=version.VERSION)
- parser.add_option("-d", "--debug", dest="debug", action="store_true",
- default=False, help="print debugging information")
- parser.add_option("-q", "--quiet", dest="quiet",
- action="store_true",
- default=False, help="Output only errors")
-
- options, args = parser.parse_args()
- safe_options = parser.get_safe_opts(options)
-
- return safe_options, options
-
-class KpasswdInstance(service.SimpleServiceInstance):
- def __init__(self):
- service.SimpleServiceInstance.__init__(self, "ipa_kpasswd")
-
-def uninstall_ipa_kpasswd():
- """
- We can't use the full service uninstaller because that will attempt
- to stop and disable the service which by now doesn't exist. We just
- want to clean up sysrestore.state to remove all references to
- ipa_kpasswd.
- """
- ipa_kpasswd = KpasswdInstance()
-
- running = ipa_kpasswd.restore_state("running")
- enabled = not ipa_kpasswd.restore_state("enabled")
-
- if enabled is not None and not enabled:
- ipa_kpasswd.remove()
-
-def backup_file(filename, ext):
- """Make a backup of filename using ext as the extension. Do not overwrite
- previous backups."""
- if not os.path.isabs(filename):
- raise ValueError("Absolute path required")
-
- backupfile = filename + ".bak"
- (reldir, file) = os.path.split(filename)
-
- while os.path.exists(backupfile):
- backupfile = backupfile + "." + str(ext)
-
- try:
- shutil.copy2(filename, backupfile)
- except IOError, e:
- if e.errno == 2: # No such file or directory
- pass
- else:
- raise e
-
-def update_conf(sub_dict, filename, template_filename):
- template = ipautil.template_file(template_filename, sub_dict)
- fd = open(filename, "w")
- fd.write(template)
- fd.close()
-
-def find_hostname():
- """Find the hostname currently configured in ipa-rewrite.conf"""
- filename=paths.HTTPD_IPA_REWRITE_CONF
-
- if not ipautil.file_exists(filename):
- return None
-
- pattern = "^[\s#]*.*https:\/\/([A-Za-z0-9\.\-]*)\/.*"
- p = re.compile(pattern)
- for line in fileinput.input(filename):
- if p.search(line):
- fileinput.close()
- return p.search(line).group(1)
- fileinput.close()
-
- raise RuntimeError("Unable to determine the fully qualified hostname from %s" % filename)
-
-def find_autoredirect(fqdn):
- """
- When upgrading ipa-rewrite.conf we need to see if the automatic redirect
- was disabled during install time (or afterward). So sift through the
- configuration file and see if we can determine the status.
-
- Returns True if autoredirect is enabled, False otherwise
- """
- filename = paths.HTTPD_IPA_REWRITE_CONF
- if os.path.exists(filename):
- pattern = "^RewriteRule \^/\$ https://%s/ipa/ui \[L,NC,R=301\]" % fqdn
- p = re.compile(pattern)
- for line in fileinput.input(filename):
- if p.search(line):
- fileinput.close()
- return True
- fileinput.close()
- return False
- return True
-
-def find_version(filename):
- """Find the version of a configuration file
-
- If no VERSION entry exists in the file, returns 0.
- If the file does not exist, returns -1.
- """
- if os.path.exists(filename):
- pattern = "^[\s#]*VERSION\s+([0-9]+)\s+.*"
- p = re.compile(pattern)
- for line in fileinput.input(filename):
- if p.search(line):
- fileinput.close()
- return p.search(line).group(1)
- fileinput.close()
-
- # no VERSION found
- return 0
- else:
- return -1
-
-def upgrade(sub_dict, filename, template, add=False):
- """
- Get the version from the current and template files and update the
- installed configuration file if there is a new template.
-
- If add is True then create a new configuration file.
- """
- old = int(find_version(filename))
- new = int(find_version(template))
-
- if old < 0 and not add:
- root_logger.error("%s not found." % filename)
- sys.exit(1)
-
- if new < 0:
- root_logger.error("%s not found." % template)
-
- if old == 0:
- # The original file does not have a VERSION entry. This means it's now
- # managed by IPA, but previously was not.
- root_logger.warning("%s is now managed by IPA. It will be "
- "overwritten. A backup of the original will be made.", filename)
-
- if old < new or (add and old == 0):
- backup_file(filename, new)
- update_conf(sub_dict, filename, template)
- root_logger.info("Upgraded %s to version %d", filename, new)
-
-def check_certs():
- """Check ca.crt is in the right place, and try to fix if not"""
- root_logger.info('[Verifying that root certificate is published]')
- if not os.path.exists(paths.CA_CRT):
- ca_file = paths.ALIAS_CACERT_ASC
- if os.path.exists(ca_file):
- old_umask = os.umask(022) # make sure its readable by httpd
- try:
- shutil.copyfile(ca_file, paths.CA_CRT)
- finally:
- os.umask(old_umask)
- else:
- root_logger.error("Missing Certification Authority file.")
- root_logger.error("You should place a copy of the CA certificate in /usr/share/ipa/html/ca.crt")
- else:
- root_logger.debug('Certificate file exists')
-
-def upgrade_pki(ca, fstore):
- """
- Update/add the dogtag proxy configuration. The IPA side of this is
- handled in ipa-pki-proxy.conf.
-
- This requires enabling SSL renegotiation.
- """
- configured_constants = dogtag.configured_constants()
- root_logger.info('[Verifying that CA proxy configuration is correct]')
- if not ca.is_configured():
- root_logger.info('CA is not configured')
- return
-
- http = httpinstance.HTTPInstance(fstore)
- http.enable_mod_nss_renegotiate()
- if not installutils.get_directive(configured_constants.CS_CFG_PATH,
- 'proxy.securePort', '=') and \
- os.path.exists(paths.PKI_SETUP_PROXY):
- # update proxy configuration with stopped dogtag to prevent corruption
- # of CS.cfg
- ipautil.run([paths.PKI_SETUP_PROXY, '-pki_instance_root=/var/lib',
- '-pki_instance_name=pki-ca','-subsystem_type=ca'])
- root_logger.debug('Proxy configuration updated')
- else:
- root_logger.debug('Proxy configuration up-to-date')
-
-def update_dbmodules(realm, filename=paths.KRB5_CONF):
- newfile = []
- found_dbrealm = False
- found_realm = False
- prefix = ''
-
- root_logger.info('[Verifying that KDC configuration is using ipa-kdb backend]')
- st = os.stat(filename)
- fd = open(filename)
-
- lines = fd.readlines()
- fd.close()
-
- if ' db_library = ipadb.so\n' in lines:
- root_logger.debug('dbmodules already updated in %s', filename)
- return
-
- for line in lines:
- if line.startswith('[dbmodules]'):
- found_dbrealm = True
- if found_dbrealm and line.find(realm) > -1:
- found_realm = True
- prefix = '#'
- if found_dbrealm and line.find('}') > -1 and found_realm:
- found_realm = False
- newfile.append('#%s' % line)
- prefix = ''
- continue
-
- newfile.append('%s%s' % (prefix, line))
-
- # Append updated dbmodules information
- newfile.append(' %s = {\n' % realm)
- newfile.append(' db_library = ipadb.so\n')
- newfile.append(' }\n')
-
- # Write out new file
- fd = open(filename, 'w')
- fd.write("".join(newfile))
- fd.close()
- root_logger.debug('%s updated', filename)
-
-def cleanup_kdc(fstore):
- """
- Clean up old KDC files if they exist. We need to remove the actual
- file and any references in the uninstall configuration.
- """
- root_logger.info('[Checking for deprecated KDC configuration files]')
- for file in ['kpasswd.keytab', 'ldappwd']:
- filename = os.path.join(paths.VAR_KERBEROS_KRB5KDC_DIR, file)
- installutils.remove_file(filename)
- if fstore.has_file(filename):
- fstore.untrack_file(filename)
- root_logger.debug('Uninstalling %s', filename)
-
-def cleanup_adtrust(fstore):
- """
- Clean up any old Samba backup files that were deprecated.
- """
-
- root_logger.info('[Checking for deprecated backups of Samba '
- 'configuration files]')
-
- for backed_up_file in [paths.SMB_CONF]:
- if fstore.has_file(backed_up_file):
- fstore.untrack_file(backed_up_file)
- root_logger.debug('Removing %s from backup', backed_up_file)
-
-
-def setup_firefox_extension(fstore):
- """Set up the Firefox configuration extension, if it's not set up yet
- """
- root_logger.info('[Setting up Firefox extension]')
- http = httpinstance.HTTPInstance(fstore)
- realm = api.env.realm
- domain = api.env.domain
- http.setup_firefox_extension(realm, domain)
-
-
-def upgrade_ipa_profile(ca, domain, fqdn):
- """
- Update the IPA Profile provided by dogtag
-
- Returns True if restart is needed, False otherwise.
- """
- root_logger.info('[Verifying that CA service certificate profile is updated]')
- if ca.is_configured():
- ski = ca.enable_subject_key_identifier()
- if ski:
- root_logger.debug('Subject Key Identifier updated.')
- else:
- root_logger.debug('Subject Key Identifier already set.')
- san = ca.enable_subject_alternative_name()
- if san:
- root_logger.debug('Subject Alternative Name updated.')
- else:
- root_logger.debug('Subject Alternative Name already set.')
- audit = ca.set_audit_renewal()
- uri = ca.set_crl_ocsp_extensions(domain, fqdn)
- if audit or ski or san or uri:
- return True
- else:
- root_logger.info('CA is not configured')
-
- return False
-
-
-def named_remove_deprecated_options():
- """
- From IPA 3.3, persistent search is a default mechanism for new DNS zone
- detection.
-
- Remove psearch, zone_refresh and cache_ttl options, as they have been
- deprecated in bind-dyndb-ldap configuration file.
-
- When some change in named.conf is done, this functions returns True.
- """
-
- root_logger.info('[Removing deprecated DNS configuration options]')
-
- if not bindinstance.named_conf_exists():
- # DNS service may not be configured
- root_logger.info('DNS is not configured')
- return False
-
- deprecated_options = ['zone_refresh', 'psearch', 'cache_ttl']
- removed_options = []
-
- try:
- # Remove all the deprecated options
- for option in deprecated_options:
- value = bindinstance.named_conf_get_directive(option)
-
- if value is not None:
- bindinstance.named_conf_set_directive(option, None)
- removed_options.append(option)
-
- except IOError, e:
- root_logger.error('Cannot modify DNS configuration in %s: %s',
- bindinstance.NAMED_CONF, e)
-
- # Log only the changed options
- if not removed_options:
- root_logger.debug('No changes made')
- return False
-
- root_logger.debug('The following configuration options have been removed: '
- '{options}'.format(options = ', '.join(removed_options)))
- return True
-
-
-def named_set_minimum_connections():
- """
- Sets the minimal number of connections.
-
- When some change in named.conf is done, this functions returns True.
- """
-
- changed = False
-
- root_logger.info('[Ensuring minimal number of connections]')
-
- if not bindinstance.named_conf_exists():
- # DNS service may not be configured
- root_logger.info('DNS is not configured')
- return changed
-
- # make sure number of connections is right
- minimum_connections = 4
-
- try:
- connections = bindinstance.named_conf_get_directive('connections')
- except IOError, e:
- root_logger.debug('Cannot retrieve connections option from %s: %s',
- bindinstance.NAMED_CONF, e)
- return changed
-
- try:
- if connections is not None:
- connections = int(connections)
- except ValueError:
- # this should not happend, but there is some bad value in
- # "connections" option, bail out
- pass
- else:
- if connections is not None and connections < minimum_connections:
- try:
- bindinstance.named_conf_set_directive('connections',
- minimum_connections)
- root_logger.debug('Connections set to %d', minimum_connections)
- except IOError, e:
- root_logger.error('Cannot update connections in %s: %s',
- bindinstance.NAMED_CONF, e)
- else:
- changed = True
-
- if not changed:
- root_logger.debug('No changes made')
-
- return changed
-
-
-def named_enable_serial_autoincrement():
- """
- Serial autoincrement is a requirement for zone transfers or DNSSEC. It
- should be enabled both for new installs and upgraded servers.
-
- When some change in named.conf is done, this functions returns True
- """
- changed = False
-
- root_logger.info('[Enabling serial autoincrement in DNS]')
-
- if not bindinstance.named_conf_exists():
- # DNS service may not be configured
- root_logger.info('DNS is not configured')
- return changed
-
- try:
- serial_autoincrement = bindinstance.named_conf_get_directive(
- 'serial_autoincrement')
- except IOError, e:
- root_logger.debug('Cannot retrieve psearch option from %s: %s',
- bindinstance.NAMED_CONF, e)
- return changed
- else:
- serial_autoincrement = None if serial_autoincrement is None \
- else serial_autoincrement.lower()
-
- # enable SOA serial autoincrement
- if not sysupgrade.get_upgrade_state('named.conf', 'autoincrement_enabled'):
- if serial_autoincrement != 'yes':
- try:
- bindinstance.named_conf_set_directive('serial_autoincrement', 'yes')
- except IOError, e:
- root_logger.error('Cannot enable serial_autoincrement in %s: %s',
- bindinstance.NAMED_CONF, e)
- return changed
- else:
- root_logger.debug('Serial autoincrement enabled')
- changed = True
- else:
- root_logger.debug('Serial autoincrement is alredy enabled')
- sysupgrade.set_upgrade_state('named.conf', 'autoincrement_enabled', True)
- else:
- root_logger.debug('Skip serial autoincrement check')
-
- return changed
-
-def named_update_gssapi_configuration():
- """
- Update GSSAPI configuration in named.conf to a recent API.
- tkey-gssapi-credential and tkey-domain is replaced with tkey-gssapi-keytab.
- Details can be found in https://fedorahosted.org/freeipa/ticket/3429.
-
- When some change in named.conf is done, this functions returns True
- """
-
- root_logger.info('[Updating GSSAPI configuration in DNS]')
-
- if not bindinstance.named_conf_exists():
- # DNS service may not be configured
- root_logger.info('DNS is not configured')
- return False
-
- if sysupgrade.get_upgrade_state('named.conf', 'gssapi_updated'):
- root_logger.debug('Skip GSSAPI configuration check')
- return False
-
- try:
- gssapi_keytab = bindinstance.named_conf_get_directive('tkey-gssapi-keytab',
- bindinstance.NAMED_SECTION_OPTIONS)
- except IOError, e:
- root_logger.error('Cannot retrieve tkey-gssapi-keytab option from %s: %s',
- bindinstance.NAMED_CONF, e)
- return False
- else:
- if gssapi_keytab:
- root_logger.debug('GSSAPI configuration already updated')
- sysupgrade.set_upgrade_state('named.conf', 'gssapi_updated', True)
- return False
-
- try:
- tkey_credential = bindinstance.named_conf_get_directive('tkey-gssapi-credential',
- bindinstance.NAMED_SECTION_OPTIONS)
- tkey_domain = bindinstance.named_conf_get_directive('tkey-domain',
- bindinstance.NAMED_SECTION_OPTIONS)
- except IOError, e:
- root_logger.error('Cannot retrieve tkey-gssapi-credential option from %s: %s',
- bindinstance.NAMED_CONF, e)
- return False
-
- if not tkey_credential or not tkey_domain:
- root_logger.error('Either tkey-gssapi-credential or tkey-domain is missing in %s. '
- 'Skip update.', bindinstance.NAMED_CONF)
- return False
-
- try:
- bindinstance.named_conf_set_directive(
- 'tkey-gssapi-credential', None,
- bindinstance.NAMED_SECTION_OPTIONS)
- bindinstance.named_conf_set_directive(
- 'tkey-domain', None,
- bindinstance.NAMED_SECTION_OPTIONS)
- bindinstance.named_conf_set_directive(
- 'tkey-gssapi-keytab', paths.NAMED_KEYTAB,
- bindinstance.NAMED_SECTION_OPTIONS)
- except IOError, e:
- root_logger.error('Cannot update GSSAPI configuration in %s: %s',
- bindinstance.NAMED_CONF, e)
- return False
- else:
- root_logger.debug('GSSAPI configuration updated')
-
- sysupgrade.set_upgrade_state('named.conf', 'gssapi_updated', True)
- return True
-
-
-def named_update_pid_file():
- """
- Make sure that named reads the pid file from the right file
- """
- root_logger.info('[Updating pid-file configuration in DNS]')
-
- if not bindinstance.named_conf_exists():
- # DNS service may not be configured
- root_logger.info('DNS is not configured')
- return False
-
- if sysupgrade.get_upgrade_state('named.conf', 'pid-file_updated'):
- root_logger.debug('Skip pid-file configuration check')
- return False
-
- try:
- pid_file = bindinstance.named_conf_get_directive('pid-file',
- bindinstance.NAMED_SECTION_OPTIONS)
- except IOError, e:
- root_logger.error('Cannot retrieve pid-file option from %s: %s',
- bindinstance.NAMED_CONF, e)
- return False
- else:
- if pid_file:
- root_logger.debug('pid-file configuration already updated')
- sysupgrade.set_upgrade_state('named.conf', 'pid-file_updated', True)
- return False
-
- try:
- bindinstance.named_conf_set_directive('pid-file', paths.NAMED_PID,
- bindinstance.NAMED_SECTION_OPTIONS)
- except IOError, e:
- root_logger.error('Cannot update pid-file configuration in %s: %s',
- bindinstance.NAMED_CONF, e)
- return False
- else:
- root_logger.debug('pid-file configuration updated')
-
- sysupgrade.set_upgrade_state('named.conf', 'pid-file_updated', True)
- return True
-
-def named_enable_dnssec():
- """
- Enable dnssec in named.conf
- """
- if not bindinstance.named_conf_exists():
- # DNS service may not be configured
- root_logger.info('DNS is not configured')
- return False
-
- if not sysupgrade.get_upgrade_state('named.conf', 'dnssec_enabled'):
- root_logger.info('[Enabling "dnssec-enable" configuration in DNS]')
- try:
- bindinstance.named_conf_set_directive('dnssec-enable', 'yes',
- bindinstance.NAMED_SECTION_OPTIONS,
- str_val=False)
- except IOError, e:
- root_logger.error('Cannot update dnssec-enable configuration in %s: %s',
- bindinstance.NAMED_CONF, e)
- return False
- else:
- root_logger.debug('dnssec-enabled in %s' % bindinstance.NAMED_CONF)
-
- sysupgrade.set_upgrade_state('named.conf', 'dnssec_enabled', True)
- return True
-
-def named_validate_dnssec():
- """
- Disable dnssec validation in named.conf
-
- We can't let enable it by default, there can be non-valid dns forwarders
- which breaks DNSSEC validation
- """
- if not bindinstance.named_conf_exists():
- # DNS service may not be configured
- root_logger.info('DNS is not configured')
- return False
-
- if (not sysupgrade.get_upgrade_state('named.conf', 'dnssec_validation_upgraded')
- and bindinstance.named_conf_get_directive(
- 'dnssec-validation', bindinstance.NAMED_SECTION_OPTIONS,
- str_val=False) is None):
- # dnssec-validation is not configured, disable it
- root_logger.info('[Disabling "dnssec-validate" configuration in DNS]')
- try:
- bindinstance.named_conf_set_directive('dnssec-validation', 'no',
- bindinstance.NAMED_SECTION_OPTIONS,
- str_val=False)
- except IOError, e:
- root_logger.error('Cannot update dnssec-validate configuration in %s: %s',
- bindinstance.NAMED_CONF, e)
- return False
- else:
- root_logger.debug('dnssec-validate already configured in %s' % bindinstance.NAMED_CONF)
-
- sysupgrade.set_upgrade_state('named.conf', 'dnssec_validation_upgraded', True)
- return True
-
-def named_bindkey_file_option():
- """
- Add options bindkey_file to named.conf
- """
- if not bindinstance.named_conf_exists():
- # DNS service may not be configured
- root_logger.info('DNS is not configured')
- return False
-
- if sysupgrade.get_upgrade_state('named.conf', 'bindkey-file_updated'):
- root_logger.debug('Skip bindkey-file configuration check')
- return False
-
- try:
- bindkey_file = bindinstance.named_conf_get_directive('bindkey-file',
- bindinstance.NAMED_SECTION_OPTIONS)
- except IOError, e:
- root_logger.error('Cannot retrieve bindkey-file option from %s: %s',
- bindinstance.NAMED_CONF, e)
- return False
- else:
- if bindkey_file:
- root_logger.debug('bindkey-file configuration already updated')
- sysupgrade.set_upgrade_state('named.conf', 'bindkey-file_updated', True)
- return False
-
- root_logger.info('[Setting "bindkeys-file" option in named.conf]')
- try:
- bindinstance.named_conf_set_directive('bindkeys-file',
- paths.NAMED_BINDKEYS_FILE,
- bindinstance.NAMED_SECTION_OPTIONS)
- except IOError, e:
- root_logger.error('Cannot update bindkeys-file configuration in %s: %s',
- bindinstance.NAMED_CONF, e)
- return False
-
-
- sysupgrade.set_upgrade_state('named.conf', 'bindkey-file_updated', True)
- return True
-
-def named_managed_keys_dir_option():
- """
- Add options managed_keys_directory to named.conf
- """
- if not bindinstance.named_conf_exists():
- # DNS service may not be configured
- root_logger.info('DNS is not configured')
- return False
-
- if sysupgrade.get_upgrade_state('named.conf', 'managed-keys-directory_updated'):
- root_logger.debug('Skip managed-keys-directory configuration check')
- return False
-
- try:
- managed_keys = bindinstance.named_conf_get_directive('managed-keys-directory',
- bindinstance.NAMED_SECTION_OPTIONS)
- except IOError, e:
- root_logger.error('Cannot retrieve managed-keys-directory option from %s: %s',
- bindinstance.NAMED_CONF, e)
- return False
- else:
- if managed_keys:
- root_logger.debug('managed_keys_directory configuration already updated')
- sysupgrade.set_upgrade_state('named.conf', 'managed-keys-directory_updated', True)
- return False
-
- root_logger.info('[Setting "managed-keys-directory" option in named.conf]')
- try:
- bindinstance.named_conf_set_directive('managed-keys-directory',
- paths.NAMED_MANAGED_KEYS_DIR,
- bindinstance.NAMED_SECTION_OPTIONS)
- except IOError, e:
- root_logger.error('Cannot update managed-keys-directory configuration in %s: %s',
- bindinstance.NAMED_CONF, e)
- return False
-
-
- sysupgrade.set_upgrade_state('named.conf', 'managed-keys-directory_updated', True)
- return True
-
-def named_root_key_include():
- """
- Add options managed_keys_directory to named.conf
- """
- if not bindinstance.named_conf_exists():
- # DNS service may not be configured
- root_logger.info('DNS is not configured')
- return False
-
- if sysupgrade.get_upgrade_state('named.conf', 'root_key_updated'):
- root_logger.debug('Skip root key configuration check')
- return False
-
- try:
- root_key = bindinstance.named_conf_include_exists(paths.NAMED_ROOT_KEY)
- except IOError, e:
- root_logger.error('Cannot check root key include in %s: %s',
- bindinstance.NAMED_CONF, e)
- return False
- else:
- if root_key:
- root_logger.debug('root keys configuration already updated')
- sysupgrade.set_upgrade_state('named.conf', 'root_key_updated', True)
- return False
-
- root_logger.info('[Including named root key in named.conf]')
- try:
- bindinstance.named_conf_add_include(paths.NAMED_ROOT_KEY)
- except IOError, e:
- root_logger.error('Cannot update named root key include in %s: %s',
- bindinstance.NAMED_CONF, e)
- return False
-
-
- sysupgrade.set_upgrade_state('named.conf', 'root_key_updated', True)
- return True
-
-def certificate_renewal_update(ca):
- """
- Update certmonger certificate renewal configuration.
- """
- dogtag_constants = dogtag.configured_constants()
-
- # bump version when requests is changed
- version = 3
- requests = (
- (
- dogtag_constants.ALIAS_DIR,
- 'auditSigningCert cert-pki-ca',
- 'dogtag-ipa-ca-renew-agent',
- 'stop_pkicad',
- 'renew_ca_cert',
- None,
- ),
- (
- dogtag_constants.ALIAS_DIR,
- 'ocspSigningCert cert-pki-ca',
- 'dogtag-ipa-ca-renew-agent',
- 'stop_pkicad',
- 'renew_ca_cert',
- None,
- ),
- (
- dogtag_constants.ALIAS_DIR,
- 'subsystemCert cert-pki-ca',
- 'dogtag-ipa-ca-renew-agent',
- 'stop_pkicad',
- 'renew_ca_cert',
- None,
- ),
- (
- dogtag_constants.ALIAS_DIR,
- 'caSigningCert cert-pki-ca',
- 'dogtag-ipa-ca-renew-agent',
- 'stop_pkicad',
- 'renew_ca_cert',
- 'ipaCACertRenewal',
- ),
- (
- paths.HTTPD_ALIAS_DIR,
- 'ipaCert',
- 'dogtag-ipa-ca-renew-agent',
- None,
- 'renew_ra_cert',
- None,
- ),
- (
- dogtag_constants.ALIAS_DIR,
- 'Server-Cert cert-pki-ca',
- 'dogtag-ipa-renew-agent',
- 'stop_pkicad',
- 'renew_ca_cert',
- None,
- ),
- )
-
- root_logger.info("[Update certmonger certificate renewal configuration to "
- "version %d]" % version)
- if not ca.is_configured():
- root_logger.info('CA is not configured')
- return False
-
- state = 'certificate_renewal_update_%d' % version
- if sysupgrade.get_upgrade_state('dogtag', state):
- return False
-
- # State not set, lets see if we are already configured
- for request in requests:
- nss_dir, nickname, ca_name, pre_command, post_command, profile = request
- criteria = {
- 'cert-database': nss_dir,
- 'cert-nickname': nickname,
- 'ca-name': ca_name,
- 'template-profile': profile,
- }
- request_id = certmonger.get_request_id(criteria)
- if request_id is None:
- break
-
- val = certmonger.get_request_value(request_id, 'cert-presave-command')
- if val is not None:
- val = val.split(' ', 1)[0]
- val = os.path.basename(val)
- if pre_command != val:
- break
-
- val = certmonger.get_request_value(request_id, 'cert-postsave-command')
- if val is not None:
- val = val.split(' ', 1)[0]
- val = os.path.basename(val)
- if post_command != val:
- break
- else:
- sysupgrade.set_upgrade_state('dogtag', state, True)
- root_logger.info("Certmonger certificate renewal configuration is "
- "already at version %d" % version)
- return False
-
- # Ok, now we need to stop tracking, then we can start tracking them
- # again with new configuration:
- ca.stop_tracking_certificates()
-
- if not sysupgrade.get_upgrade_state('dogtag',
- 'certificate_renewal_update_1'):
- filename = paths.CERTMONGER_CAS_CA_RENEWAL
- if os.path.exists(filename):
- with installutils.stopped_service('certmonger'):
- root_logger.info("Removing %s" % filename)
- installutils.remove_file(filename)
-
- ca.configure_certmonger_renewal()
- ca.configure_renewal()
- ca.configure_agent_renewal()
- ca.track_servercert()
-
- sysupgrade.set_upgrade_state('dogtag', state, True)
- root_logger.info("Certmonger certificate renewal configuration updated to "
- "version %d" % version)
- return True
-
-def copy_crl_file(old_path, new_path=None):
- """
- Copy CRL to new location, update permissions and SELinux context
- """
- if new_path is None:
- filename = os.path.basename(old_path)
- new_path = os.path.join(dogtag.configured_constants().CRL_PUBLISH_PATH,
- filename)
- root_logger.debug('copy_crl_file: %s -> %s', old_path, new_path)
-
- if os.path.islink(old_path):
- # update symlink to the most most recent CRL file
- filename = os.path.basename(os.readlink(old_path))
- realpath = os.path.join(dogtag.configured_constants().CRL_PUBLISH_PATH,
- filename)
- root_logger.debug('copy_crl_file: Create symlink %s -> %s',
- new_path, realpath)
- os.symlink(realpath, new_path)
- else:
- shutil.copy2(old_path, new_path)
- pent = pwd.getpwnam(cainstance.PKI_USER)
- os.chown(new_path, pent.pw_uid, pent.pw_gid)
-
- tasks.restore_context(new_path)
-
-def migrate_crl_publish_dir(ca):
- """
- Move CRL publish dir from /var/lib/pki-ca/publish to IPA controlled tree:
- /var/lib/ipa/pki-ca/publish
- """
- root_logger.info('[Migrate CRL publish directory]')
- if sysupgrade.get_upgrade_state('dogtag', 'moved_crl_publish_dir'):
- root_logger.info('CRL tree already moved')
- return False
-
- if not ca.is_configured():
- root_logger.info('CA is not configured')
- return False
-
- caconfig = dogtag.configured_constants()
-
- try:
- old_publish_dir = installutils.get_directive(caconfig.CS_CFG_PATH,
- 'ca.publish.publisher.instance.FileBaseCRLPublisher.directory',
- separator='=')
- except OSError, e:
- root_logger.error('Cannot read CA configuration file "%s": %s',
- caconfig.CS_CFG_PATH, e)
- return False
-
- # Prepare target publish dir (creation, permissions, SELinux context)
- # Run this every update to ensure proper values
- publishdir = ca.prepare_crl_publish_dir()
-
- if old_publish_dir == caconfig.CRL_PUBLISH_PATH:
- # publish dir is already updated
- root_logger.info('Publish directory already set to new location')
- sysupgrade.set_upgrade_state('dogtag', 'moved_crl_publish_dir', True)
- return False
-
- # Copy all CRLs to new directory
- root_logger.info('Copy all CRLs to new publish directory')
- try:
- crl_files_unsorted = cainstance.get_crl_files(old_publish_dir)
- except OSError, e:
- root_logger.error('Cannot move CRL files to new directory: %s', e)
- else:
- # Move CRL files at the end of the list to make sure that the actual
- # CRL files are copied first
- crl_files = sorted(crl_files_unsorted,
- key=lambda f: os.path.islink(f))
- for f in crl_files:
- try:
- copy_crl_file(f)
- except Exception, e:
- root_logger.error('Cannot move CRL file to new directory: %s', e)
-
- try:
- installutils.set_directive(caconfig.CS_CFG_PATH,
- 'ca.publish.publisher.instance.FileBaseCRLPublisher.directory',
- publishdir, quotes=False, separator='=')
- except OSError, e:
- root_logger.error('Cannot update CA configuration file "%s": %s',
- caconfig.CS_CFG_PATH, e)
- return False
- sysupgrade.set_upgrade_state('dogtag', 'moved_crl_publish_dir', True)
- root_logger.info('CRL publish directory has been migrated, '
- 'request pki-ca restart')
- return True
-
-
-def ca_enable_pkix(ca):
- root_logger.info('[Enable PKIX certificate path discovery and validation]')
- if sysupgrade.get_upgrade_state('dogtag', 'pkix_enabled'):
- root_logger.info('PKIX already enabled')
- return False
-
- if not ca.is_configured():
- root_logger.info('CA is not configured')
- return False
-
- ca.enable_pkix()
- sysupgrade.set_upgrade_state('dogtag', 'pkix_enabled', True)
-
- return True
-
-
-def add_ca_dns_records():
- root_logger.info('[Add missing CA DNS records]')
-
- if sysupgrade.get_upgrade_state('dns', 'ipa_ca_records'):
- root_logger.info('IPA CA DNS records already processed')
- return
-
- if not api.Backend.ldap2.isconnected():
- try:
- api.Backend.ldap2.connect(autobind=True)
- except ipalib.errors.PublicError, e:
- root_logger.error(
- "Cannot connect to LDAP to add DNS records: %s", e)
- return
-
- ret = api.Command['dns_is_enabled']()
- if not ret['result']:
- root_logger.info('DNS is not configured')
- sysupgrade.set_upgrade_state('dns', 'ipa_ca_records', True)
- return
-
- bind = bindinstance.BindInstance()
-
- bind.convert_ipa_ca_cnames(api.env.domain)
-
- # DNS is enabled, so let bindinstance find out if CA is enabled
- # and let it add the record in that case
- bind.add_ipa_ca_dns_records(api.env.host, api.env.domain,
- ca_configured=None)
-
- sysupgrade.set_upgrade_state('dns', 'ipa_ca_records', True)
-
-
-def find_subject_base():
- """
- Try to find the current value of certificate subject base.
- See the docstring in dsinstance.DsInstance for details.
- """
- subject_base = dsinstance.DsInstance().find_subject_base()
-
- if subject_base:
- sysupgrade.set_upgrade_state(
- 'certmap.conf',
- 'subject_base',
- subject_base
- )
- return subject_base
-
- root_logger.error('Unable to determine certificate subject base. '
- 'certmap.conf will not be updated.')
-
-
-def uninstall_selfsign(ds, http):
- root_logger.info('[Removing self-signed CA]')
- """Replace self-signed CA by a CA-less install"""
- if api.env.ra_plugin != 'selfsign':
- root_logger.debug('Self-signed CA is not installed')
- return
-
- root_logger.warning(
- 'Removing self-signed CA. Certificates will need to managed manually.')
- p = ConfigParser.SafeConfigParser()
- p.read(paths.IPA_DEFAULT_CONF)
- p.set('global', 'enable_ra', 'False')
- p.set('global', 'ra_plugin', 'none')
- with open(paths.IPA_DEFAULT_CONF, 'w') as f:
- p.write(f)
-
- ds.stop_tracking_certificates()
- http.stop_tracking_certificates()
-
-
-def mask_named_regular():
- """Disable named, we need to run only named-pkcs11, running both named and
- named-pkcs can cause unexpected errors"""
- if sysupgrade.get_upgrade_state('dns', 'regular_named_masked'):
- return False
-
- sysupgrade.set_upgrade_state('dns', 'regular_named_masked', True)
-
- if bindinstance.named_conf_exists():
- root_logger.info('[Masking named]')
- named = services.service('named-regular')
- try:
- named.stop()
- except Exception as e:
- root_logger.warning('Unable to stop named service (%s)', e)
-
- try:
- named.mask()
- except Exception as e:
- root_logger.warning('Unable to mask named service (%s)', e)
-
- return True
-
- return False
-
-
-def fix_dyndb_ldap_workdir_permissions():
- """Fix dyndb-ldap working dir permissions. DNSSEC daemons requires it"""
- if sysupgrade.get_upgrade_state('dns', 'dyndb_ipa_workdir_perm'):
- return
-
- if bindinstance.named_conf_exists():
- root_logger.info('[Fix bind-dyndb-ldap IPA working directory]')
- dnskeysync = dnskeysyncinstance.DNSKeySyncInstance()
- dnskeysync.set_dyndb_ldap_workdir_permissions()
-
- sysupgrade.set_upgrade_state('dns', 'dyndb_ipa_workdir_perm', True)
-
-
-def fix_schema_file_syntax():
- """Fix syntax errors in schema files
-
- https://fedorahosted.org/freeipa/ticket/3578
- """
- root_logger.info('[Fix DS schema file syntax]')
-
- # This is not handled by normal schema updates, because pre-1.3.2 DS will
- # ignore (auto-fix) these syntax errors, and 1.3.2 and above will choke on
- # them before checking dynamic schema updates.
-
- if sysupgrade.get_upgrade_state('ds', 'fix_schema_syntax'):
- root_logger.info('Syntax already fixed')
- return
-
- serverid = installutils.realm_to_serverid(api.env.realm)
- ds_dir = dsinstance.config_dirname(serverid)
-
- # 1. 60ipadns.ldif: Add parenthesis to idnsRecord
-
- filename = os.path.join(ds_dir, 'schema', '60ipadns.ldif')
- result_lines = []
- with open(filename) as file:
- for line in file:
- line = line.strip('\n')
- if (line.startswith('objectClasses:') and
- "NAME 'idnsRecord'" in line and
- line.count('(') == 2 and
- line.count(')') == 1):
- root_logger.debug('Add closing parenthesis in idnsRecord')
- line += ' )'
- result_lines.append(line)
-
- with open(filename, 'w') as file:
- file.write('\n'.join(result_lines))
-
- # 2. 65ipasudo.ldif: Remove extra dollar from ipaSudoRule
-
- filename = os.path.join(ds_dir, 'schema', '65ipasudo.ldif')
- result_lines = []
- with open(filename) as file:
- for line in file:
- line = line.strip('\n')
- if (line.startswith('objectClasses:') and
- "NAME 'ipaSudoRule'" in line):
- root_logger.debug('Remove extra dollar sign in ipaSudoRule')
- line = line.replace('$$', '$')
- result_lines.append(line)
-
- with open(filename, 'w') as file:
- file.write('\n'.join(result_lines))
-
- # Done
-
- sysupgrade.set_upgrade_state('ds', 'fix_schema_syntax', True)
-
-
-def set_sssd_domain_option(option, value):
- sssdconfig = SSSDConfig.SSSDConfig()
- sssdconfig.import_config()
- domain = sssdconfig.get_domain(str(api.env.domain))
- domain.set_option(option, value)
- sssdconfig.save_domain(domain)
- sssdconfig.write(paths.SSSD_CONF)
-
-
-def remove_ds_ra_cert(subject_base):
- root_logger.info('[Removing RA cert from DS NSS database]')
-
- if sysupgrade.get_upgrade_state('ds', 'remove_ra_cert'):
- root_logger.info('RA cert already removed')
- return
-
- dbdir = dsinstance.config_dirname(
- installutils.realm_to_serverid(api.env.realm))
- dsdb = certs.CertDB(api.env.realm, nssdir=dbdir, subject_base=subject_base)
-
- nickname = 'CN=IPA RA,%s' % subject_base
- cert = dsdb.get_cert_from_db(nickname)
- if cert:
- dsdb.delete_cert(nickname)
-
- sysupgrade.set_upgrade_state('ds', 'remove_ra_cert', True)
-
-
-def fix_trust_flags():
- root_logger.info('[Fixing trust flags in %s]' % paths.HTTPD_ALIAS_DIR)
-
- if sysupgrade.get_upgrade_state('http', 'fix_trust_flags'):
- root_logger.info("Trust flags already processed")
- return
-
- if not api.Backend.ldap2.isconnected():
- try:
- api.Backend.ldap2.connect(autobind=True)
- except ipalib.errors.PublicError, e:
- root_logger.error("Cannot connect to LDAP: %s", e)
- return
-
- if not api.Command.ca_is_enabled()['result']:
- root_logger.info("CA is not enabled")
- return
-
- db = certs.CertDB(api.env.realm)
- nickname = certdb.get_ca_nickname(api.env.realm)
- cert = db.get_cert_from_db(nickname)
- if cert:
- db.trust_root_cert(nickname, 'CT,C,C')
-
- sysupgrade.set_upgrade_state('http', 'fix_trust_flags', True)
-
-
-def update_mod_nss_protocol(http):
- root_logger.info('[Updating mod_nss protocol versions]')
-
- if sysupgrade.get_upgrade_state('nss.conf', 'protocol_updated_tls12'):
- root_logger.info("Protocol versions already updated")
- return
-
- http.set_mod_nss_protocol()
-
- sysupgrade.set_upgrade_state('nss.conf', 'protocol_updated_tls12', True)
-
-
-def main():
- """
- Get some basics about the system. If getting those basics fail then
- this is likely because the machine isn't currently an IPA server so
- exit gracefully.
- """
-
- if not os.geteuid()==0:
- sys.exit("\nYou must be root to run this script.\n")
-
- if not installutils.is_ipa_configured():
- sys.exit(0)
-
- safe_options, options = parse_options()
-
- verbose = not options.quiet
- if options.debug:
- console_format = '%(levelname)s: %(message)s'
- else:
- console_format = '%(message)s'
-
- standard_logging_setup(paths.IPAUPGRADE_LOG, debug=options.debug,
- verbose=verbose, console_format=console_format, filemode='a')
- root_logger.debug('%s was invoked with options: %s' % (sys.argv[0], safe_options))
- root_logger.debug('IPA version %s' % version.VENDOR_VERSION)
-
- fstore = sysrestore.FileStore(paths.SYSRESTORE)
-
- api.bootstrap(context='restart', in_server=True)
- api.finalize()
-
- fqdn = find_hostname()
- if fqdn is None:
- # ipa-rewrite.conf doesn't exist, nothing to do
- sys.exit(0)
-
- # Ok, we are an IPA server, do the additional tests
-
- check_certs()
-
- auto_redirect = find_autoredirect(fqdn)
- configured_constants = dogtag.configured_constants()
- sub_dict = dict(
- REALM=api.env.realm,
- FQDN=fqdn,
- AUTOREDIR='' if auto_redirect else '#',
- CRL_PUBLISH_PATH=configured_constants.CRL_PUBLISH_PATH,
- DOGTAG_PORT=configured_constants.AJP_PORT,
- CLONE='#'
- )
-
- subject_base = find_subject_base()
- if subject_base:
- sub_dict['SUBJECT_BASE'] = subject_base
-
- ca = cainstance.CAInstance(api.env.realm, certs.NSS_DIR)
- ca.backup_config()
-
- with installutils.stopped_service(configured_constants.SERVICE_NAME,
- configured_constants.PKI_INSTANCE_NAME):
- # migrate CRL publish dir before the location in ipa.conf is updated
- ca_restart = migrate_crl_publish_dir(ca)
-
- if ca.is_configured():
- crl = installutils.get_directive(configured_constants.CS_CFG_PATH,
- 'ca.crl.MasterCRL.enableCRLUpdates', '=')
- sub_dict['CLONE']='#' if crl.lower() == 'true' else ''
-
- ds_serverid = installutils.realm_to_serverid(api.env.realm)
- ds_dirname = dsinstance.config_dirname(ds_serverid)
-
- upgrade(sub_dict, paths.HTTPD_IPA_CONF, ipautil.SHARE_DIR + "ipa.conf")
- upgrade(sub_dict, paths.HTTPD_IPA_REWRITE_CONF, ipautil.SHARE_DIR + "ipa-rewrite.conf")
- if ca.is_configured():
- upgrade(sub_dict, paths.HTTPD_IPA_PKI_PROXY_CONF, ipautil.SHARE_DIR + "ipa-pki-proxy.conf", add=True)
- else:
- if ipautil.file_exists(paths.HTTPD_IPA_PKI_PROXY_CONF):
- os.remove(paths.HTTPD_IPA_PKI_PROXY_CONF)
- if subject_base:
- upgrade(
- sub_dict,
- os.path.join(ds_dirname, "certmap.conf"),
- os.path.join(ipautil.SHARE_DIR, "certmap.conf.template")
- )
- upgrade_pki(ca, fstore)
-
- ca.configure_certmonger_renewal_guard()
-
- update_dbmodules(api.env.realm)
- uninstall_ipa_kpasswd()
-
- removed_sysconfig_file = paths.SYSCONFIG_HTTPD
- if fstore.has_file(removed_sysconfig_file):
- root_logger.info('Restoring %s as it is no longer required',
- removed_sysconfig_file)
- fstore.restore_file(removed_sysconfig_file)
-
- http = httpinstance.HTTPInstance(fstore)
- http.configure_selinux_for_httpd()
- http.change_mod_nss_port_from_http()
- http.configure_certmonger_renewal_guard()
-
- http.stop()
- update_mod_nss_protocol(http)
- fix_trust_flags()
- http.start()
-
- ds = dsinstance.DsInstance()
- ds.configure_dirsrv_ccache()
-
- ds.stop(ds_serverid)
- fix_schema_file_syntax()
- remove_ds_ra_cert(subject_base)
- ds.start(ds_serverid)
-
- uninstall_selfsign(ds, http)
-
- simple_service_list = (
- (memcacheinstance.MemcacheInstance(), 'MEMCACHE'),
- (otpdinstance.OtpdInstance(), 'OTPD'),
- )
-
- for service, ldap_name in simple_service_list:
- service.ldapi = True
- try:
- if not service.is_configured():
- # 389-ds needs to be running to create the memcache instance
- # because we record the new service in cn=masters.
- ds.start()
- service.create_instance(ldap_name, fqdn, None,
- ipautil.realm_to_suffix(api.env.realm),
- realm=api.env.realm)
- except ipalib.errors.DuplicateEntry:
- pass
-
- # install DNSKeySync service only if DNS is configured on server
- if bindinstance.named_conf_exists():
- dnskeysyncd = dnskeysyncinstance.DNSKeySyncInstance(fstore, ldapi=True)
- if not dnskeysyncd.is_configured():
- ds.start()
- dnskeysyncd.create_instance(fqdn, api.env.realm)
- dnskeysyncd.start_dnskeysyncd()
-
- cleanup_kdc(fstore)
- cleanup_adtrust(fstore)
- setup_firefox_extension(fstore)
- add_ca_dns_records()
-
- # Any of the following functions returns True iff the named.conf file
- # has been altered
- named_conf_changes = (
- named_remove_deprecated_options(),
- named_set_minimum_connections(),
- named_enable_serial_autoincrement(),
- named_update_gssapi_configuration(),
- named_update_pid_file(),
- named_enable_dnssec(),
- named_validate_dnssec(),
- named_bindkey_file_option(),
- named_managed_keys_dir_option(),
- named_root_key_include(),
- mask_named_regular(),
- fix_dyndb_ldap_workdir_permissions(),
- )
-
- if any(named_conf_changes):
- # configuration has changed, restart the name server
- root_logger.info('Changes to named.conf have been made, restart named')
- bind = bindinstance.BindInstance(fstore)
- try:
- bind.restart()
- except ipautil.CalledProcessError, e:
- root_logger.error("Failed to restart %s: %s", bind.service_name, e)
-
- ca_restart = any([
- ca_restart,
- upgrade_ipa_profile(ca, api.env.domain, fqdn),
- certificate_renewal_update(ca),
- ca_enable_pkix(ca),
- ])
-
- if ca_restart:
- root_logger.info('pki-ca configuration changed, restart pki-ca')
- try:
- ca.restart(dogtag.configured_constants().PKI_INSTANCE_NAME)
- except ipautil.CalledProcessError, e:
- root_logger.error("Failed to restart %s: %s", ca.service_name, e)
- set_sssd_domain_option('ipa_server_mode', 'True')
if __name__ == '__main__':
- installutils.run_script(main, operation_name='ipa-upgradeconfig')
+ sys.exit("Please run the 'ipa-server-upgrade' command to upgrade the "
+ "IPA server.")
diff --git a/ipaserver/install/ipa_server_upgrade.py b/ipaserver/install/ipa_server_upgrade.py
index 6236e2ed0..31772dc71 100644
--- a/ipaserver/install/ipa_server_upgrade.py
+++ b/ipaserver/install/ipa_server_upgrade.py
@@ -12,6 +12,7 @@ from ipaplatform.paths import paths
from ipapython import admintool, ipautil
from ipaserver.install import dsinstance
from ipaserver.install import installutils
+from ipaserver.install.server import upgrade_configuration
from ipaserver.install.upgradeinstance import IPAUpgrade
from ipaserver.install.ldapupdate import BadSyntax
@@ -95,16 +96,10 @@ class ServerUpgrade(admintool.AdminTool):
# store new data version after upgrade
installutils.store_version()
- # FIXME: remove this when new installer will be ready
- # execute upgrade of configuration
- cmd = ['ipa-upgradeconfig', ]
- if options.verbose:
- cmd.append('--debug')
- if options.quiet:
- cmd.append('--quiet')
-
- self.log.info('Executing ipa-upgradeconfig, please wait')
- ipautil.run(cmd)
+ print 'Upgrading IPA services'
+ self.log.info('Upgrading the configuration of the IPA services')
+ upgrade_configuration()
+ self.log.info('The IPA services were upgraded')
def handle_error(self, exception):
return installutils.handle_error(exception, self.log_file_name)
diff --git a/ipaserver/install/server.py b/ipaserver/install/server.py
new file mode 100644
index 000000000..c08b74828
--- /dev/null
+++ b/ipaserver/install/server.py
@@ -0,0 +1,1376 @@
+#
+# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
+#
+
+import re
+import os
+import shutil
+import pwd
+import fileinput
+import ConfigParser
+
+from ipalib import api
+import SSSDConfig
+import ipalib.util
+import ipalib.errors
+from ipaplatform import services
+from ipaplatform.tasks import tasks
+from ipapython import ipautil, sysrestore, version, certdb
+from ipapython.ipa_log_manager import *
+from ipapython import certmonger
+from ipapython import dogtag
+from ipaplatform.paths import paths
+from ipaserver.install import installutils
+from ipaserver.install import dsinstance
+from ipaserver.install import httpinstance
+from ipaserver.install import memcacheinstance
+from ipaserver.install import bindinstance
+from ipaserver.install import service
+from ipaserver.install import cainstance
+from ipaserver.install import certs
+from ipaserver.install import otpdinstance
+from ipaserver.install import sysupgrade
+from ipaserver.install import dnskeysyncinstance
+
+class KpasswdInstance(service.SimpleServiceInstance):
+ def __init__(self):
+ service.SimpleServiceInstance.__init__(self, "ipa_kpasswd")
+
+def uninstall_ipa_kpasswd():
+ """
+ We can't use the full service uninstaller because that will attempt
+ to stop and disable the service which by now doesn't exist. We just
+ want to clean up sysrestore.state to remove all references to
+ ipa_kpasswd.
+ """
+ ipa_kpasswd = KpasswdInstance()
+
+ running = ipa_kpasswd.restore_state("running")
+ enabled = not ipa_kpasswd.restore_state("enabled")
+
+ if enabled is not None and not enabled:
+ ipa_kpasswd.remove()
+
+def backup_file(filename, ext):
+ """Make a backup of filename using ext as the extension. Do not overwrite
+ previous backups."""
+ if not os.path.isabs(filename):
+ raise ValueError("Absolute path required")
+
+ backupfile = filename + ".bak"
+ (reldir, file) = os.path.split(filename)
+
+ while os.path.exists(backupfile):
+ backupfile = backupfile + "." + str(ext)
+
+ try:
+ shutil.copy2(filename, backupfile)
+ except IOError as e:
+ if e.errno == 2: # No such file or directory
+ pass
+ else:
+ raise e
+
+def update_conf(sub_dict, filename, template_filename):
+ template = ipautil.template_file(template_filename, sub_dict)
+ fd = open(filename, "w")
+ fd.write(template)
+ fd.close()
+
+def find_hostname():
+ """Find the hostname currently configured in ipa-rewrite.conf"""
+ filename=paths.HTTPD_IPA_REWRITE_CONF
+
+ if not ipautil.file_exists(filename):
+ return None
+
+ pattern = "^[\s#]*.*https:\/\/([A-Za-z0-9\.\-]*)\/.*"
+ p = re.compile(pattern)
+ for line in fileinput.input(filename):
+ if p.search(line):
+ fileinput.close()
+ return p.search(line).group(1)
+ fileinput.close()
+
+ raise RuntimeError("Unable to determine the fully qualified hostname from %s" % filename)
+
+def find_autoredirect(fqdn):
+ """
+ When upgrading ipa-rewrite.conf we need to see if the automatic redirect
+ was disabled during install time (or afterward). So sift through the
+ configuration file and see if we can determine the status.
+
+ Returns True if autoredirect is enabled, False otherwise
+ """
+ filename = paths.HTTPD_IPA_REWRITE_CONF
+ if os.path.exists(filename):
+ pattern = "^RewriteRule \^/\$ https://%s/ipa/ui \[L,NC,R=301\]" % fqdn
+ p = re.compile(pattern)
+ for line in fileinput.input(filename):
+ if p.search(line):
+ fileinput.close()
+ return True
+ fileinput.close()
+ return False
+ return True
+
+def find_version(filename):
+ """Find the version of a configuration file
+
+ If no VERSION entry exists in the file, returns 0.
+ If the file does not exist, returns -1.
+ """
+ if os.path.exists(filename):
+ pattern = "^[\s#]*VERSION\s+([0-9]+)\s+.*"
+ p = re.compile(pattern)
+ for line in fileinput.input(filename):
+ if p.search(line):
+ fileinput.close()
+ return p.search(line).group(1)
+ fileinput.close()
+
+ # no VERSION found
+ return 0
+ else:
+ return -1
+
+def upgrade(sub_dict, filename, template, add=False):
+ """
+ Get the version from the current and template files and update the
+ installed configuration file if there is a new template.
+
+ If add is True then create a new configuration file.
+ """
+ old = int(find_version(filename))
+ new = int(find_version(template))
+
+ if old < 0 and not add:
+ root_logger.error("%s not found." % filename)
+ raise RuntimeError("%s not found." % filename)
+
+ if new < 0:
+ root_logger.error("%s not found." % template)
+
+ if old == 0:
+ # The original file does not have a VERSION entry. This means it's now
+ # managed by IPA, but previously was not.
+ root_logger.warning("%s is now managed by IPA. It will be "
+ "overwritten. A backup of the original will be made.", filename)
+
+ if old < new or (add and old == 0):
+ backup_file(filename, new)
+ update_conf(sub_dict, filename, template)
+ root_logger.info("Upgraded %s to version %d", filename, new)
+
+def check_certs():
+ """Check ca.crt is in the right place, and try to fix if not"""
+ root_logger.info('[Verifying that root certificate is published]')
+ if not os.path.exists(paths.CA_CRT):
+ ca_file = paths.ALIAS_CACERT_ASC
+ if os.path.exists(ca_file):
+ old_umask = os.umask(022) # make sure its readable by httpd
+ try:
+ shutil.copyfile(ca_file, paths.CA_CRT)
+ finally:
+ os.umask(old_umask)
+ else:
+ root_logger.error("Missing Certification Authority file.")
+ root_logger.error("You should place a copy of the CA certificate in /usr/share/ipa/html/ca.crt")
+ else:
+ root_logger.debug('Certificate file exists')
+
+def upgrade_pki(ca, fstore):
+ """
+ Update/add the dogtag proxy configuration. The IPA side of this is
+ handled in ipa-pki-proxy.conf.
+
+ This requires enabling SSL renegotiation.
+ """
+ configured_constants = dogtag.configured_constants()
+ root_logger.info('[Verifying that CA proxy configuration is correct]')
+ if not ca.is_configured():
+ root_logger.info('CA is not configured')
+ return
+
+ http = httpinstance.HTTPInstance(fstore)
+ http.enable_mod_nss_renegotiate()
+ if not installutils.get_directive(configured_constants.CS_CFG_PATH,
+ 'proxy.securePort', '=') and \
+ os.path.exists(paths.PKI_SETUP_PROXY):
+ # update proxy configuration with stopped dogtag to prevent corruption
+ # of CS.cfg
+ ipautil.run([paths.PKI_SETUP_PROXY, '-pki_instance_root=/var/lib',
+ '-pki_instance_name=pki-ca','-subsystem_type=ca'])
+ root_logger.debug('Proxy configuration updated')
+ else:
+ root_logger.debug('Proxy configuration up-to-date')
+
+def update_dbmodules(realm, filename=paths.KRB5_CONF):
+ newfile = []
+ found_dbrealm = False
+ found_realm = False
+ prefix = ''
+
+ root_logger.info('[Verifying that KDC configuration is using ipa-kdb backend]')
+ st = os.stat(filename)
+ fd = open(filename)
+
+ lines = fd.readlines()
+ fd.close()
+
+ if ' db_library = ipadb.so\n' in lines:
+ root_logger.debug('dbmodules already updated in %s', filename)
+ return
+
+ for line in lines:
+ if line.startswith('[dbmodules]'):
+ found_dbrealm = True
+ if found_dbrealm and line.find(realm) > -1:
+ found_realm = True
+ prefix = '#'
+ if found_dbrealm and line.find('}') > -1 and found_realm:
+ found_realm = False
+ newfile.append('#%s' % line)
+ prefix = ''
+ continue
+
+ newfile.append('%s%s' % (prefix, line))
+
+ # Append updated dbmodules information
+ newfile.append(' %s = {\n' % realm)
+ newfile.append(' db_library = ipadb.so\n')
+ newfile.append(' }\n')
+
+ # Write out new file
+ fd = open(filename, 'w')
+ fd.write("".join(newfile))
+ fd.close()
+ root_logger.debug('%s updated', filename)
+
+def cleanup_kdc(fstore):
+ """
+ Clean up old KDC files if they exist. We need to remove the actual
+ file and any references in the uninstall configuration.
+ """
+ root_logger.info('[Checking for deprecated KDC configuration files]')
+ for file in ['kpasswd.keytab', 'ldappwd']:
+ filename = os.path.join(paths.VAR_KERBEROS_KRB5KDC_DIR, file)
+ installutils.remove_file(filename)
+ if fstore.has_file(filename):
+ fstore.untrack_file(filename)
+ root_logger.debug('Uninstalling %s', filename)
+
+def cleanup_adtrust(fstore):
+ """
+ Clean up any old Samba backup files that were deprecated.
+ """
+
+ root_logger.info('[Checking for deprecated backups of Samba '
+ 'configuration files]')
+
+ for backed_up_file in [paths.SMB_CONF]:
+ if fstore.has_file(backed_up_file):
+ fstore.untrack_file(backed_up_file)
+ root_logger.debug('Removing %s from backup', backed_up_file)
+
+
+def setup_firefox_extension(fstore):
+ """Set up the Firefox configuration extension, if it's not set up yet
+ """
+ root_logger.info('[Setting up Firefox extension]')
+ http = httpinstance.HTTPInstance(fstore)
+ realm = api.env.realm
+ domain = api.env.domain
+ http.setup_firefox_extension(realm, domain)
+
+
+def upgrade_ipa_profile(ca, domain, fqdn):
+ """
+ Update the IPA Profile provided by dogtag
+
+ Returns True if restart is needed, False otherwise.
+ """
+ root_logger.info('[Verifying that CA service certificate profile is updated]')
+ if ca.is_configured():
+ ski = ca.enable_subject_key_identifier()
+ if ski:
+ root_logger.debug('Subject Key Identifier updated.')
+ else:
+ root_logger.debug('Subject Key Identifier already set.')
+ san = ca.enable_subject_alternative_name()
+ if san:
+ root_logger.debug('Subject Alternative Name updated.')
+ else:
+ root_logger.debug('Subject Alternative Name already set.')
+ audit = ca.set_audit_renewal()
+ uri = ca.set_crl_ocsp_extensions(domain, fqdn)
+ if audit or ski or san or uri:
+ return True
+ else:
+ root_logger.info('CA is not configured')
+
+ return False
+
+
+def named_remove_deprecated_options():
+ """
+ From IPA 3.3, persistent search is a default mechanism for new DNS zone
+ detection.
+
+ Remove psearch, zone_refresh and cache_ttl options, as they have been
+ deprecated in bind-dyndb-ldap configuration file.
+
+ When some change in named.conf is done, this functions returns True.
+ """
+
+ root_logger.info('[Removing deprecated DNS configuration options]')
+
+ if not bindinstance.named_conf_exists():
+ # DNS service may not be configured
+ root_logger.info('DNS is not configured')
+ return False
+
+ deprecated_options = ['zone_refresh', 'psearch', 'cache_ttl']
+ removed_options = []
+
+ try:
+ # Remove all the deprecated options
+ for option in deprecated_options:
+ value = bindinstance.named_conf_get_directive(option)
+
+ if value is not None:
+ bindinstance.named_conf_set_directive(option, None)
+ removed_options.append(option)
+
+ except IOError as e:
+ root_logger.error('Cannot modify DNS configuration in %s: %s',
+ bindinstance.NAMED_CONF, e)
+
+ # Log only the changed options
+ if not removed_options:
+ root_logger.debug('No changes made')
+ return False
+
+ root_logger.debug('The following configuration options have been removed: '
+ '{options}'.format(options = ', '.join(removed_options)))
+ return True
+
+
+def named_set_minimum_connections():
+ """
+ Sets the minimal number of connections.
+
+ When some change in named.conf is done, this functions returns True.
+ """
+
+ changed = False
+
+ root_logger.info('[Ensuring minimal number of connections]')
+
+ if not bindinstance.named_conf_exists():
+ # DNS service may not be configured
+ root_logger.info('DNS is not configured')
+ return changed
+
+ # make sure number of connections is right
+ minimum_connections = 4
+
+ try:
+ connections = bindinstance.named_conf_get_directive('connections')
+ except IOError as e:
+ root_logger.debug('Cannot retrieve connections option from %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return changed
+
+ try:
+ if connections is not None:
+ connections = int(connections)
+ except ValueError:
+ # this should not happend, but there is some bad value in
+ # "connections" option, bail out
+ pass
+ else:
+ if connections is not None and connections < minimum_connections:
+ try:
+ bindinstance.named_conf_set_directive('connections',
+ minimum_connections)
+ root_logger.debug('Connections set to %d', minimum_connections)
+ except IOError, e:
+ root_logger.error('Cannot update connections in %s: %s',
+ bindinstance.NAMED_CONF, e)
+ else:
+ changed = True
+
+ if not changed:
+ root_logger.debug('No changes made')
+
+ return changed
+
+
+def named_enable_serial_autoincrement():
+ """
+ Serial autoincrement is a requirement for zone transfers or DNSSEC. It
+ should be enabled both for new installs and upgraded servers.
+
+ When some change in named.conf is done, this functions returns True
+ """
+ changed = False
+
+ root_logger.info('[Enabling serial autoincrement in DNS]')
+
+ if not bindinstance.named_conf_exists():
+ # DNS service may not be configured
+ root_logger.info('DNS is not configured')
+ return changed
+
+ try:
+ serial_autoincrement = bindinstance.named_conf_get_directive(
+ 'serial_autoincrement')
+ except IOError as e:
+ root_logger.debug('Cannot retrieve psearch option from %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return changed
+ else:
+ serial_autoincrement = None if serial_autoincrement is None \
+ else serial_autoincrement.lower()
+
+ # enable SOA serial autoincrement
+ if not sysupgrade.get_upgrade_state('named.conf', 'autoincrement_enabled'):
+ if serial_autoincrement != 'yes':
+ try:
+ bindinstance.named_conf_set_directive('serial_autoincrement',
+ 'yes')
+ except IOError, e:
+ root_logger.error('Cannot enable serial_autoincrement in %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return changed
+ else:
+ root_logger.debug('Serial autoincrement enabled')
+ changed = True
+ else:
+ root_logger.debug('Serial autoincrement is alredy enabled')
+ sysupgrade.set_upgrade_state('named.conf', 'autoincrement_enabled', True)
+ else:
+ root_logger.debug('Skip serial autoincrement check')
+
+ return changed
+
+def named_update_gssapi_configuration():
+ """
+ Update GSSAPI configuration in named.conf to a recent API.
+ tkey-gssapi-credential and tkey-domain is replaced with tkey-gssapi-keytab.
+ Details can be found in https://fedorahosted.org/freeipa/ticket/3429.
+
+ When some change in named.conf is done, this functions returns True
+ """
+
+ root_logger.info('[Updating GSSAPI configuration in DNS]')
+
+ if not bindinstance.named_conf_exists():
+ # DNS service may not be configured
+ root_logger.info('DNS is not configured')
+ return False
+
+ if sysupgrade.get_upgrade_state('named.conf', 'gssapi_updated'):
+ root_logger.debug('Skip GSSAPI configuration check')
+ return False
+
+ try:
+ gssapi_keytab = bindinstance.named_conf_get_directive('tkey-gssapi-keytab',
+ bindinstance.NAMED_SECTION_OPTIONS)
+ except IOError, e:
+ root_logger.error('Cannot retrieve tkey-gssapi-keytab option from %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return False
+ else:
+ if gssapi_keytab:
+ root_logger.debug('GSSAPI configuration already updated')
+ sysupgrade.set_upgrade_state('named.conf', 'gssapi_updated', True)
+ return False
+
+ try:
+ tkey_credential = bindinstance.named_conf_get_directive('tkey-gssapi-credential',
+ bindinstance.NAMED_SECTION_OPTIONS)
+ tkey_domain = bindinstance.named_conf_get_directive('tkey-domain',
+ bindinstance.NAMED_SECTION_OPTIONS)
+ except IOError, e:
+ root_logger.error('Cannot retrieve tkey-gssapi-credential option from %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return False
+
+ if not tkey_credential or not tkey_domain:
+ root_logger.error('Either tkey-gssapi-credential or tkey-domain is missing in %s. '
+ 'Skip update.', bindinstance.NAMED_CONF)
+ return False
+
+ try:
+ bindinstance.named_conf_set_directive(
+ 'tkey-gssapi-credential', None,
+ bindinstance.NAMED_SECTION_OPTIONS)
+ bindinstance.named_conf_set_directive(
+ 'tkey-domain', None,
+ bindinstance.NAMED_SECTION_OPTIONS)
+ bindinstance.named_conf_set_directive(
+ 'tkey-gssapi-keytab', paths.NAMED_KEYTAB,
+ bindinstance.NAMED_SECTION_OPTIONS)
+ except IOError, e:
+ root_logger.error('Cannot update GSSAPI configuration in %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return False
+ else:
+ root_logger.debug('GSSAPI configuration updated')
+
+ sysupgrade.set_upgrade_state('named.conf', 'gssapi_updated', True)
+ return True
+
+
+def named_update_pid_file():
+ """
+ Make sure that named reads the pid file from the right file
+ """
+ root_logger.info('[Updating pid-file configuration in DNS]')
+
+ if not bindinstance.named_conf_exists():
+ # DNS service may not be configured
+ root_logger.info('DNS is not configured')
+ return False
+
+ if sysupgrade.get_upgrade_state('named.conf', 'pid-file_updated'):
+ root_logger.debug('Skip pid-file configuration check')
+ return False
+
+ try:
+ pid_file = bindinstance.named_conf_get_directive('pid-file',
+ bindinstance.NAMED_SECTION_OPTIONS)
+ except IOError, e:
+ root_logger.error('Cannot retrieve pid-file option from %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return False
+ else:
+ if pid_file:
+ root_logger.debug('pid-file configuration already updated')
+ sysupgrade.set_upgrade_state('named.conf', 'pid-file_updated', True)
+ return False
+
+ try:
+ bindinstance.named_conf_set_directive('pid-file', paths.NAMED_PID,
+ bindinstance.NAMED_SECTION_OPTIONS)
+ except IOError, e:
+ root_logger.error('Cannot update pid-file configuration in %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return False
+ else:
+ root_logger.debug('pid-file configuration updated')
+
+ sysupgrade.set_upgrade_state('named.conf', 'pid-file_updated', True)
+ return True
+
+def named_enable_dnssec():
+ """
+ Enable dnssec in named.conf
+ """
+ if not bindinstance.named_conf_exists():
+ # DNS service may not be configured
+ root_logger.info('DNS is not configured')
+ return False
+
+ if not sysupgrade.get_upgrade_state('named.conf', 'dnssec_enabled'):
+ root_logger.info('[Enabling "dnssec-enable" configuration in DNS]')
+ try:
+ bindinstance.named_conf_set_directive('dnssec-enable', 'yes',
+ bindinstance.NAMED_SECTION_OPTIONS,
+ str_val=False)
+ except IOError, e:
+ root_logger.error('Cannot update dnssec-enable configuration in %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return False
+ else:
+ root_logger.debug('dnssec-enabled in %s' % bindinstance.NAMED_CONF)
+
+ sysupgrade.set_upgrade_state('named.conf', 'dnssec_enabled', True)
+ return True
+
+def named_validate_dnssec():
+ """
+ Disable dnssec validation in named.conf
+
+ We can't let enable it by default, there can be non-valid dns forwarders
+ which breaks DNSSEC validation
+ """
+ if not bindinstance.named_conf_exists():
+ # DNS service may not be configured
+ root_logger.info('DNS is not configured')
+ return False
+
+ if (not sysupgrade.get_upgrade_state('named.conf', 'dnssec_validation_upgraded')
+ and bindinstance.named_conf_get_directive(
+ 'dnssec-validation', bindinstance.NAMED_SECTION_OPTIONS,
+ str_val=False) is None):
+ # dnssec-validation is not configured, disable it
+ root_logger.info('[Disabling "dnssec-validate" configuration in DNS]')
+ try:
+ bindinstance.named_conf_set_directive('dnssec-validation', 'no',
+ bindinstance.NAMED_SECTION_OPTIONS,
+ str_val=False)
+ except IOError, e:
+ root_logger.error('Cannot update dnssec-validate configuration in %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return False
+ else:
+ root_logger.debug('dnssec-validate already configured in %s' % bindinstance.NAMED_CONF)
+
+ sysupgrade.set_upgrade_state('named.conf', 'dnssec_validation_upgraded', True)
+ return True
+
+def named_bindkey_file_option():
+ """
+ Add options bindkey_file to named.conf
+ """
+ if not bindinstance.named_conf_exists():
+ # DNS service may not be configured
+ root_logger.info('DNS is not configured')
+ return False
+
+ if sysupgrade.get_upgrade_state('named.conf', 'bindkey-file_updated'):
+ root_logger.debug('Skip bindkey-file configuration check')
+ return False
+
+ try:
+ bindkey_file = bindinstance.named_conf_get_directive('bindkey-file',
+ bindinstance.NAMED_SECTION_OPTIONS)
+ except IOError, e:
+ root_logger.error('Cannot retrieve bindkey-file option from %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return False
+ else:
+ if bindkey_file:
+ root_logger.debug('bindkey-file configuration already updated')
+ sysupgrade.set_upgrade_state('named.conf', 'bindkey-file_updated', True)
+ return False
+
+ root_logger.info('[Setting "bindkeys-file" option in named.conf]')
+ try:
+ bindinstance.named_conf_set_directive('bindkeys-file',
+ paths.NAMED_BINDKEYS_FILE,
+ bindinstance.NAMED_SECTION_OPTIONS)
+ except IOError, e:
+ root_logger.error('Cannot update bindkeys-file configuration in %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return False
+
+
+ sysupgrade.set_upgrade_state('named.conf', 'bindkey-file_updated', True)
+ return True
+
+def named_managed_keys_dir_option():
+ """
+ Add options managed_keys_directory to named.conf
+ """
+ if not bindinstance.named_conf_exists():
+ # DNS service may not be configured
+ root_logger.info('DNS is not configured')
+ return False
+
+ if sysupgrade.get_upgrade_state('named.conf', 'managed-keys-directory_updated'):
+ root_logger.debug('Skip managed-keys-directory configuration check')
+ return False
+
+ try:
+ managed_keys = bindinstance.named_conf_get_directive('managed-keys-directory',
+ bindinstance.NAMED_SECTION_OPTIONS)
+ except IOError, e:
+ root_logger.error('Cannot retrieve managed-keys-directory option from %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return False
+ else:
+ if managed_keys:
+ root_logger.debug('managed_keys_directory configuration already updated')
+ sysupgrade.set_upgrade_state('named.conf', 'managed-keys-directory_updated', True)
+ return False
+
+ root_logger.info('[Setting "managed-keys-directory" option in named.conf]')
+ try:
+ bindinstance.named_conf_set_directive('managed-keys-directory',
+ paths.NAMED_MANAGED_KEYS_DIR,
+ bindinstance.NAMED_SECTION_OPTIONS)
+ except IOError, e:
+ root_logger.error('Cannot update managed-keys-directory configuration in %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return False
+
+
+ sysupgrade.set_upgrade_state('named.conf', 'managed-keys-directory_updated', True)
+ return True
+
+def named_root_key_include():
+ """
+ Add options managed_keys_directory to named.conf
+ """
+ if not bindinstance.named_conf_exists():
+ # DNS service may not be configured
+ root_logger.info('DNS is not configured')
+ return False
+
+ if sysupgrade.get_upgrade_state('named.conf', 'root_key_updated'):
+ root_logger.debug('Skip root key configuration check')
+ return False
+
+ try:
+ root_key = bindinstance.named_conf_include_exists(paths.NAMED_ROOT_KEY)
+ except IOError, e:
+ root_logger.error('Cannot check root key include in %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return False
+ else:
+ if root_key:
+ root_logger.debug('root keys configuration already updated')
+ sysupgrade.set_upgrade_state('named.conf', 'root_key_updated', True)
+ return False
+
+ root_logger.info('[Including named root key in named.conf]')
+ try:
+ bindinstance.named_conf_add_include(paths.NAMED_ROOT_KEY)
+ except IOError, e:
+ root_logger.error('Cannot update named root key include in %s: %s',
+ bindinstance.NAMED_CONF, e)
+ return False
+
+
+ sysupgrade.set_upgrade_state('named.conf', 'root_key_updated', True)
+ return True
+
+def certificate_renewal_update(ca):
+ """
+ Update certmonger certificate renewal configuration.
+ """
+ dogtag_constants = dogtag.configured_constants()
+
+ # bump version when requests is changed
+ version = 3
+ requests = (
+ (
+ dogtag_constants.ALIAS_DIR,
+ 'auditSigningCert cert-pki-ca',
+ 'dogtag-ipa-ca-renew-agent',
+ 'stop_pkicad',
+ 'renew_ca_cert',
+ None,
+ ),
+ (
+ dogtag_constants.ALIAS_DIR,
+ 'ocspSigningCert cert-pki-ca',
+ 'dogtag-ipa-ca-renew-agent',
+ 'stop_pkicad',
+ 'renew_ca_cert',
+ None,
+ ),
+ (
+ dogtag_constants.ALIAS_DIR,
+ 'subsystemCert cert-pki-ca',
+ 'dogtag-ipa-ca-renew-agent',
+ 'stop_pkicad',
+ 'renew_ca_cert',
+ None,
+ ),
+ (
+ dogtag_constants.ALIAS_DIR,
+ 'caSigningCert cert-pki-ca',
+ 'dogtag-ipa-ca-renew-agent',
+ 'stop_pkicad',
+ 'renew_ca_cert',
+ 'ipaCACertRenewal',
+ ),
+ (
+ paths.HTTPD_ALIAS_DIR,
+ 'ipaCert',
+ 'dogtag-ipa-ca-renew-agent',
+ None,
+ 'renew_ra_cert',
+ None,
+ ),
+ (
+ dogtag_constants.ALIAS_DIR,
+ 'Server-Cert cert-pki-ca',
+ 'dogtag-ipa-renew-agent',
+ 'stop_pkicad',
+ 'renew_ca_cert',
+ None,
+ ),
+ )
+
+ root_logger.info("[Update certmonger certificate renewal configuration to "
+ "version %d]" % version)
+ if not ca.is_configured():
+ root_logger.info('CA is not configured')
+ return False
+
+ state = 'certificate_renewal_update_%d' % version
+ if sysupgrade.get_upgrade_state('dogtag', state):
+ return False
+
+ # State not set, lets see if we are already configured
+ for request in requests:
+ nss_dir, nickname, ca_name, pre_command, post_command, profile = request
+ criteria = {
+ 'cert-database': nss_dir,
+ 'cert-nickname': nickname,
+ 'ca-name': ca_name,
+ 'template-profile': profile,
+ }
+ request_id = certmonger.get_request_id(criteria)
+ if request_id is None:
+ break
+
+ val = certmonger.get_request_value(request_id, 'cert-presave-command')
+ if val is not None:
+ val = val.split(' ', 1)[0]
+ val = os.path.basename(val)
+ if pre_command != val:
+ break
+
+ val = certmonger.get_request_value(request_id, 'cert-postsave-command')
+ if val is not None:
+ val = val.split(' ', 1)[0]
+ val = os.path.basename(val)
+ if post_command != val:
+ break
+ else:
+ sysupgrade.set_upgrade_state('dogtag', state, True)
+ root_logger.info("Certmonger certificate renewal configuration is "
+ "already at version %d" % version)
+ return False
+
+ # Ok, now we need to stop tracking, then we can start tracking them
+ # again with new configuration:
+ ca.stop_tracking_certificates()
+
+ if not sysupgrade.get_upgrade_state('dogtag',
+ 'certificate_renewal_update_1'):
+ filename = paths.CERTMONGER_CAS_CA_RENEWAL
+ if os.path.exists(filename):
+ with installutils.stopped_service('certmonger'):
+ root_logger.info("Removing %s" % filename)
+ installutils.remove_file(filename)
+
+ ca.configure_certmonger_renewal()
+ ca.configure_renewal()
+ ca.configure_agent_renewal()
+ ca.track_servercert()
+
+ sysupgrade.set_upgrade_state('dogtag', state, True)
+ root_logger.info("Certmonger certificate renewal configuration updated to "
+ "version %d" % version)
+ return True
+
+def copy_crl_file(old_path, new_path=None):
+ """
+ Copy CRL to new location, update permissions and SELinux context
+ """
+ if new_path is None:
+ filename = os.path.basename(old_path)
+ new_path = os.path.join(dogtag.configured_constants().CRL_PUBLISH_PATH,
+ filename)
+ root_logger.debug('copy_crl_file: %s -> %s', old_path, new_path)
+
+ if os.path.islink(old_path):
+ # update symlink to the most most recent CRL file
+ filename = os.path.basename(os.readlink(old_path))
+ realpath = os.path.join(dogtag.configured_constants().CRL_PUBLISH_PATH,
+ filename)
+ root_logger.debug('copy_crl_file: Create symlink %s -> %s',
+ new_path, realpath)
+ os.symlink(realpath, new_path)
+ else:
+ shutil.copy2(old_path, new_path)
+ pent = pwd.getpwnam(cainstance.PKI_USER)
+ os.chown(new_path, pent.pw_uid, pent.pw_gid)
+
+ tasks.restore_context(new_path)
+
+def migrate_crl_publish_dir(ca):
+ """
+ Move CRL publish dir from /var/lib/pki-ca/publish to IPA controlled tree:
+ /var/lib/ipa/pki-ca/publish
+ """
+ root_logger.info('[Migrate CRL publish directory]')
+ if sysupgrade.get_upgrade_state('dogtag', 'moved_crl_publish_dir'):
+ root_logger.info('CRL tree already moved')
+ return False
+
+ if not ca.is_configured():
+ root_logger.info('CA is not configured')
+ return False
+
+ caconfig = dogtag.configured_constants()
+
+ try:
+ old_publish_dir = installutils.get_directive(caconfig.CS_CFG_PATH,
+ 'ca.publish.publisher.instance.FileBaseCRLPublisher.directory',
+ separator='=')
+ except OSError, e:
+ root_logger.error('Cannot read CA configuration file "%s": %s',
+ caconfig.CS_CFG_PATH, e)
+ return False
+
+ # Prepare target publish dir (creation, permissions, SELinux context)
+ # Run this every update to ensure proper values
+ publishdir = ca.prepare_crl_publish_dir()
+
+ if old_publish_dir == caconfig.CRL_PUBLISH_PATH:
+ # publish dir is already updated
+ root_logger.info('Publish directory already set to new location')
+ sysupgrade.set_upgrade_state('dogtag', 'moved_crl_publish_dir', True)
+ return False
+
+ # Copy all CRLs to new directory
+ root_logger.info('Copy all CRLs to new publish directory')
+ try:
+ crl_files_unsorted = cainstance.get_crl_files(old_publish_dir)
+ except OSError, e:
+ root_logger.error('Cannot move CRL files to new directory: %s', e)
+ else:
+ # Move CRL files at the end of the list to make sure that the actual
+ # CRL files are copied first
+ crl_files = sorted(crl_files_unsorted,
+ key=lambda f: os.path.islink(f))
+ for f in crl_files:
+ try:
+ copy_crl_file(f)
+ except Exception as e:
+ root_logger.error('Cannot move CRL file to new directory: %s', e)
+
+ try:
+ installutils.set_directive(caconfig.CS_CFG_PATH,
+ 'ca.publish.publisher.instance.FileBaseCRLPublisher.directory',
+ publishdir, quotes=False, separator='=')
+ except OSError as e:
+ root_logger.error('Cannot update CA configuration file "%s": %s',
+ caconfig.CS_CFG_PATH, e)
+ return False
+ sysupgrade.set_upgrade_state('dogtag', 'moved_crl_publish_dir', True)
+ root_logger.info('CRL publish directory has been migrated, '
+ 'request pki-ca restart')
+ return True
+
+
+def ca_enable_pkix(ca):
+ root_logger.info('[Enable PKIX certificate path discovery and validation]')
+ if sysupgrade.get_upgrade_state('dogtag', 'pkix_enabled'):
+ root_logger.info('PKIX already enabled')
+ return False
+
+ if not ca.is_configured():
+ root_logger.info('CA is not configured')
+ return False
+
+ ca.enable_pkix()
+ sysupgrade.set_upgrade_state('dogtag', 'pkix_enabled', True)
+
+ return True
+
+
+def add_ca_dns_records():
+ root_logger.info('[Add missing CA DNS records]')
+
+ if sysupgrade.get_upgrade_state('dns', 'ipa_ca_records'):
+ root_logger.info('IPA CA DNS records already processed')
+ return
+
+ if not api.Backend.ldap2.isconnected():
+ try:
+ api.Backend.ldap2.connect(autobind=True)
+ except ipalib.errors.PublicError, e:
+ root_logger.error(
+ "Cannot connect to LDAP to add DNS records: %s", e)
+ return
+
+ ret = api.Command['dns_is_enabled']()
+ if not ret['result']:
+ root_logger.info('DNS is not configured')
+ sysupgrade.set_upgrade_state('dns', 'ipa_ca_records', True)
+ return
+
+ bind = bindinstance.BindInstance()
+
+ bind.convert_ipa_ca_cnames(api.env.domain)
+
+ # DNS is enabled, so let bindinstance find out if CA is enabled
+ # and let it add the record in that case
+ bind.add_ipa_ca_dns_records(api.env.host, api.env.domain,
+ ca_configured=None)
+
+ sysupgrade.set_upgrade_state('dns', 'ipa_ca_records', True)
+
+
+def find_subject_base():
+ """
+ Try to find the current value of certificate subject base.
+ See the docstring in dsinstance.DsInstance for details.
+ """
+ subject_base = dsinstance.DsInstance().find_subject_base()
+
+ if subject_base:
+ sysupgrade.set_upgrade_state(
+ 'certmap.conf',
+ 'subject_base',
+ subject_base
+ )
+ return subject_base
+
+ root_logger.error('Unable to determine certificate subject base. '
+ 'certmap.conf will not be updated.')
+
+
+def uninstall_selfsign(ds, http):
+ root_logger.info('[Removing self-signed CA]')
+ """Replace self-signed CA by a CA-less install"""
+ if api.env.ra_plugin != 'selfsign':
+ root_logger.debug('Self-signed CA is not installed')
+ return
+
+ root_logger.warning(
+ 'Removing self-signed CA. Certificates will need to managed manually.')
+ p = ConfigParser.SafeConfigParser()
+ p.read(paths.IPA_DEFAULT_CONF)
+ p.set('global', 'enable_ra', 'False')
+ p.set('global', 'ra_plugin', 'none')
+ with open(paths.IPA_DEFAULT_CONF, 'w') as f:
+ p.write(f)
+
+ ds.stop_tracking_certificates()
+ http.stop_tracking_certificates()
+
+
+def mask_named_regular():
+ """Disable named, we need to run only named-pkcs11, running both named and
+ named-pkcs can cause unexpected errors"""
+ if sysupgrade.get_upgrade_state('dns', 'regular_named_masked'):
+ return False
+
+ sysupgrade.set_upgrade_state('dns', 'regular_named_masked', True)
+
+ if bindinstance.named_conf_exists():
+ root_logger.info('[Masking named]')
+ named = services.service('named-regular')
+ try:
+ named.stop()
+ except Exception as e:
+ root_logger.warning('Unable to stop named service (%s)', e)
+
+ try:
+ named.mask()
+ except Exception as e:
+ root_logger.warning('Unable to mask named service (%s)', e)
+
+ return True
+
+ return False
+
+
+def fix_dyndb_ldap_workdir_permissions():
+ """Fix dyndb-ldap working dir permissions. DNSSEC daemons requires it"""
+ if sysupgrade.get_upgrade_state('dns', 'dyndb_ipa_workdir_perm'):
+ return
+
+ if bindinstance.named_conf_exists():
+ root_logger.info('[Fix bind-dyndb-ldap IPA working directory]')
+ dnskeysync = dnskeysyncinstance.DNSKeySyncInstance()
+ dnskeysync.set_dyndb_ldap_workdir_permissions()
+
+ sysupgrade.set_upgrade_state('dns', 'dyndb_ipa_workdir_perm', True)
+
+
+def fix_schema_file_syntax():
+ """Fix syntax errors in schema files
+
+ https://fedorahosted.org/freeipa/ticket/3578
+ """
+ root_logger.info('[Fix DS schema file syntax]')
+
+ # This is not handled by normal schema updates, because pre-1.3.2 DS will
+ # ignore (auto-fix) these syntax errors, and 1.3.2 and above will choke on
+ # them before checking dynamic schema updates.
+
+ if sysupgrade.get_upgrade_state('ds', 'fix_schema_syntax'):
+ root_logger.info('Syntax already fixed')
+ return
+
+ serverid = installutils.realm_to_serverid(api.env.realm)
+ ds_dir = dsinstance.config_dirname(serverid)
+
+ # 1. 60ipadns.ldif: Add parenthesis to idnsRecord
+
+ filename = os.path.join(ds_dir, 'schema', '60ipadns.ldif')
+ result_lines = []
+ with open(filename) as file:
+ for line in file:
+ line = line.strip('\n')
+ if (line.startswith('objectClasses:') and
+ "NAME 'idnsRecord'" in line and
+ line.count('(') == 2 and
+ line.count(')') == 1):
+ root_logger.debug('Add closing parenthesis in idnsRecord')
+ line += ' )'
+ result_lines.append(line)
+
+ with open(filename, 'w') as file:
+ file.write('\n'.join(result_lines))
+
+ # 2. 65ipasudo.ldif: Remove extra dollar from ipaSudoRule
+
+ filename = os.path.join(ds_dir, 'schema', '65ipasudo.ldif')
+ result_lines = []
+ with open(filename) as file:
+ for line in file:
+ line = line.strip('\n')
+ if (line.startswith('objectClasses:') and
+ "NAME 'ipaSudoRule'" in line):
+ root_logger.debug('Remove extra dollar sign in ipaSudoRule')
+ line = line.replace('$$', '$')
+ result_lines.append(line)
+
+ with open(filename, 'w') as file:
+ file.write('\n'.join(result_lines))
+
+ # Done
+
+ sysupgrade.set_upgrade_state('ds', 'fix_schema_syntax', True)
+
+
+def set_sssd_domain_option(option, value):
+ sssdconfig = SSSDConfig.SSSDConfig()
+ sssdconfig.import_config()
+ domain = sssdconfig.get_domain(str(api.env.domain))
+ domain.set_option(option, value)
+ sssdconfig.save_domain(domain)
+ sssdconfig.write(paths.SSSD_CONF)
+
+
+def remove_ds_ra_cert(subject_base):
+ root_logger.info('[Removing RA cert from DS NSS database]')
+
+ if sysupgrade.get_upgrade_state('ds', 'remove_ra_cert'):
+ root_logger.info('RA cert already removed')
+ return
+
+ dbdir = dsinstance.config_dirname(
+ installutils.realm_to_serverid(api.env.realm))
+ dsdb = certs.CertDB(api.env.realm, nssdir=dbdir, subject_base=subject_base)
+
+ nickname = 'CN=IPA RA,%s' % subject_base
+ cert = dsdb.get_cert_from_db(nickname)
+ if cert:
+ dsdb.delete_cert(nickname)
+
+ sysupgrade.set_upgrade_state('ds', 'remove_ra_cert', True)
+
+
+def fix_trust_flags():
+ root_logger.info('[Fixing trust flags in %s]' % paths.HTTPD_ALIAS_DIR)
+
+ if sysupgrade.get_upgrade_state('http', 'fix_trust_flags'):
+ root_logger.info("Trust flags already processed")
+ return
+
+ if not api.Backend.ldap2.isconnected():
+ try:
+ api.Backend.ldap2.connect(autobind=True)
+ except ipalib.errors.PublicError, e:
+ root_logger.error("Cannot connect to LDAP: %s", e)
+ return
+
+ if not api.Command.ca_is_enabled()['result']:
+ root_logger.info("CA is not enabled")
+ return
+
+ db = certs.CertDB(api.env.realm)
+ nickname = certdb.get_ca_nickname(api.env.realm)
+ cert = db.get_cert_from_db(nickname)
+ if cert:
+ db.trust_root_cert(nickname, 'CT,C,C')
+
+ sysupgrade.set_upgrade_state('http', 'fix_trust_flags', True)
+
+
+def update_mod_nss_protocol(http):
+ root_logger.info('[Updating mod_nss protocol versions]')
+
+ if sysupgrade.get_upgrade_state('nss.conf', 'protocol_updated_tls12'):
+ root_logger.info("Protocol versions already updated")
+ return
+
+ http.set_mod_nss_protocol()
+
+ sysupgrade.set_upgrade_state('nss.conf', 'protocol_updated_tls12', True)
+
+
+def upgrade_configuration():
+ """
+ Execute configuration upgrade of the IPA services
+ """
+
+ root_logger.debug('IPA version %s' % version.VENDOR_VERSION)
+
+ fstore = sysrestore.FileStore(paths.SYSRESTORE)
+
+ fqdn = find_hostname()
+ if fqdn is None:
+ # ipa-rewrite.conf doesn't exist, nothing to do
+ raise RuntimeError("ipa-rewrite.conf doesn't exists (is this server?)")
+
+ # Ok, we are an IPA server, do the additional tests
+
+ check_certs()
+
+ auto_redirect = find_autoredirect(fqdn)
+ configured_constants = dogtag.configured_constants()
+ sub_dict = dict(
+ REALM=api.env.realm,
+ FQDN=fqdn,
+ AUTOREDIR='' if auto_redirect else '#',
+ CRL_PUBLISH_PATH=configured_constants.CRL_PUBLISH_PATH,
+ DOGTAG_PORT=configured_constants.AJP_PORT,
+ CLONE='#'
+ )
+
+ subject_base = find_subject_base()
+ if subject_base:
+ sub_dict['SUBJECT_BASE'] = subject_base
+
+ ca = cainstance.CAInstance(api.env.realm, certs.NSS_DIR)
+ ca.backup_config()
+
+ with installutils.stopped_service(configured_constants.SERVICE_NAME,
+ configured_constants.PKI_INSTANCE_NAME):
+ # migrate CRL publish dir before the location in ipa.conf is updated
+ ca_restart = migrate_crl_publish_dir(ca)
+
+ if ca.is_configured():
+ crl = installutils.get_directive(configured_constants.CS_CFG_PATH,
+ 'ca.crl.MasterCRL.enableCRLUpdates', '=')
+ sub_dict['CLONE']='#' if crl.lower() == 'true' else ''
+
+ ds_serverid = installutils.realm_to_serverid(api.env.realm)
+ ds_dirname = dsinstance.config_dirname(ds_serverid)
+
+ upgrade(sub_dict, paths.HTTPD_IPA_CONF, ipautil.SHARE_DIR + "ipa.conf")
+ upgrade(sub_dict, paths.HTTPD_IPA_REWRITE_CONF,
+ ipautil.SHARE_DIR + "ipa-rewrite.conf")
+ if ca.is_configured():
+ upgrade(sub_dict, paths.HTTPD_IPA_PKI_PROXY_CONF,
+ ipautil.SHARE_DIR + "ipa-pki-proxy.conf", add=True)
+ else:
+ if ipautil.file_exists(paths.HTTPD_IPA_PKI_PROXY_CONF):
+ os.remove(paths.HTTPD_IPA_PKI_PROXY_CONF)
+ if subject_base:
+ upgrade(
+ sub_dict,
+ os.path.join(ds_dirname, "certmap.conf"),
+ os.path.join(ipautil.SHARE_DIR, "certmap.conf.template")
+ )
+ upgrade_pki(ca, fstore)
+
+ ca.configure_certmonger_renewal_guard()
+
+ update_dbmodules(api.env.realm)
+ uninstall_ipa_kpasswd()
+
+ removed_sysconfig_file = paths.SYSCONFIG_HTTPD
+ if fstore.has_file(removed_sysconfig_file):
+ root_logger.info('Restoring %s as it is no longer required',
+ removed_sysconfig_file)
+ fstore.restore_file(removed_sysconfig_file)
+
+ http = httpinstance.HTTPInstance(fstore)
+ http.configure_selinux_for_httpd()
+ http.change_mod_nss_port_from_http()
+ http.configure_certmonger_renewal_guard()
+
+ http.stop()
+ update_mod_nss_protocol(http)
+ fix_trust_flags()
+ http.start()
+
+ ds = dsinstance.DsInstance()
+ ds.configure_dirsrv_ccache()
+
+ ds.stop(ds_serverid)
+ fix_schema_file_syntax()
+ remove_ds_ra_cert(subject_base)
+ ds.start(ds_serverid)
+
+ uninstall_selfsign(ds, http)
+
+ simple_service_list = (
+ (memcacheinstance.MemcacheInstance(), 'MEMCACHE'),
+ (otpdinstance.OtpdInstance(), 'OTPD'),
+ )
+
+ for service, ldap_name in simple_service_list:
+ service.ldapi = True
+ try:
+ if not service.is_configured():
+ # 389-ds needs to be running to create the memcache instance
+ # because we record the new service in cn=masters.
+ ds.start()
+ service.create_instance(ldap_name, fqdn, None,
+ ipautil.realm_to_suffix(api.env.realm),
+ realm=api.env.realm)
+ except ipalib.errors.DuplicateEntry:
+ pass
+
+ # install DNSKeySync service only if DNS is configured on server
+ if bindinstance.named_conf_exists():
+ dnskeysyncd = dnskeysyncinstance.DNSKeySyncInstance(fstore,
+ ldapi=True)
+ if not dnskeysyncd.is_configured():
+ ds.start()
+ dnskeysyncd.create_instance(fqdn, api.env.realm)
+ dnskeysyncd.start_dnskeysyncd()
+
+ cleanup_kdc(fstore)
+ cleanup_adtrust(fstore)
+ setup_firefox_extension(fstore)
+ add_ca_dns_records()
+
+ # Any of the following functions returns True iff the named.conf file
+ # has been altered
+ named_conf_changes = (
+ named_remove_deprecated_options(),
+ named_set_minimum_connections(),
+ named_enable_serial_autoincrement(),
+ named_update_gssapi_configuration(),
+ named_update_pid_file(),
+ named_enable_dnssec(),
+ named_validate_dnssec(),
+ named_bindkey_file_option(),
+ named_managed_keys_dir_option(),
+ named_root_key_include(),
+ mask_named_regular(),
+ fix_dyndb_ldap_workdir_permissions(),
+ )
+
+ if any(named_conf_changes):
+ # configuration has changed, restart the name server
+ root_logger.info('Changes to named.conf have been made, restart named')
+ bind = bindinstance.BindInstance(fstore)
+ try:
+ bind.restart()
+ except ipautil.CalledProcessError as e:
+ root_logger.error("Failed to restart %s: %s", bind.service_name, e)
+
+ ca_restart = any([
+ ca_restart,
+ upgrade_ipa_profile(ca, api.env.domain, fqdn),
+ certificate_renewal_update(ca),
+ ca_enable_pkix(ca),
+ ])
+
+ if ca_restart:
+ root_logger.info('pki-ca configuration changed, restart pki-ca')
+ try:
+ ca.restart(dogtag.configured_constants().PKI_INSTANCE_NAME)
+ except ipautil.CalledProcessError as e:
+ root_logger.error("Failed to restart %s: %s", ca.service_name, e)
+
+ set_sssd_domain_option('ipa_server_mode', 'True')