def restart(self, instance_name="", capture_output=True, wait=True): ipautil.run([paths.SYSTEMCTL, "restart", self.service_instance(instance_name)], skip_output=not capture_output) if wait and self.is_running(instance_name): self.wait_for_open_ports(self.service_instance(instance_name))
def run_signtool(self, args, stdin=None): with open(self.passwd_fname, "r") as f: password = f.readline() new_args = [paths.SIGNTOOL, "-d", self.secdir, "-p", password] new_args = new_args + args ipautil.run(new_args, stdin)
def __setup_principal(self): try: api.Command.service_add(unicode(self.cifs_principal)) # Add the principal to the 'adtrust agents' group # as 389-ds only operates with GroupOfNames, we have to use # the principal's proper dn as defined in self.cifs_agent try: current = self.admin_conn.get_entry(self.smb_dn) members = current.get('member', []) if not(self.cifs_agent in members): current["member"] = members + [self.cifs_agent] self.admin_conn.update_entry(self.smb_dn, current) except errors.NotFound: entry = self.admin_conn.make_entry( self.smb_dn, objectclass=["top", "GroupOfNames"], cn=[self.smb_dn['cn']], member=[self.cifs_agent], ) self.admin_conn.add_entry(entry) except Exception: # CIFS principal already exists, it is not the first time # adtrustinstance is managed # That's fine, we we'll re-extract the key again. pass self.clean_samba_keytab() try: ipautil.run(["ipa-getkeytab", "--server", self.fqdn, "--principal", self.cifs_principal, "-k", self.samba_keytab]) except ipautil.CalledProcessError: root_logger.critical("Failed to add key for %s" % self.cifs_principal)
def import_key(args, tmpdir): """Export key and certificate from a PKCS#12 file to key and cert files. """ data = json.load(args.importfile) password = data['export password'] p12data = base64.b64decode(data['pkcs12 data']) pk12pwfile = os.path.join(tmpdir, 'passwd') with open(pk12pwfile, 'w') as f: f.write(password) pk12file = os.path.join(tmpdir, 'import.p12') with open(pk12file, 'wb') as f: f.write(p12data) # get the certificate from the file cmd = [ paths.OPENSSL, 'pkcs12', '-in', pk12file, '-clcerts', '-nokeys', '-out', args.certfile, '-password', 'file:{pk12pwfile}'.format(pk12pwfile=pk12pwfile), ] ipautil.run(cmd, umask=0o027) # get the private key from the file cmd = [ paths.OPENSSL, 'pkcs12', '-in', pk12file, '-nocerts', '-nodes', '-out', args.keyfile, '-password', 'file:{pk12pwfile}'.format(pk12pwfile=pk12pwfile), ] ipautil.run(cmd, umask=0o027)
def export_key(args, tmpdir): """Export cert and private from PEM files as PKCS#12 file. The PKCS#12 file is encrypted with a password. """ pk12file = os.path.join(tmpdir, 'export.p12') password = ipautil.ipa_generate_password() pk12pwfile = os.path.join(tmpdir, 'passwd') with open(pk12pwfile, 'w') as f: f.write(password) # OpenSSL does not support pkcs12 export of a cert without key ipautil.run([ paths.OPENSSL, 'pkcs12', '-export', '-in', args.certfile, '-out', pk12file, '-inkey', args.keyfile, '-password', 'file:{pk12pwfile}'.format(pk12pwfile=pk12pwfile), ]) with open(pk12file, 'rb') as f: p12data = f.read() data = { 'export password': password, 'pkcs12 data': p12data, } common.json_dump(data, args.exportfile)
def change_admin_password(self, password): root_logger.debug("Changing admin password") dirname = config_dirname(self.serverid) dmpwdfile = "" admpwdfile = "" try: (dmpwdfd, dmpwdfile) = tempfile.mkstemp(dir='/var/lib/ipa') os.write(dmpwdfd, self.dm_password) os.close(dmpwdfd) (admpwdfd, admpwdfile) = tempfile.mkstemp(dir='/var/lib/ipa') os.write(admpwdfd, password) os.close(admpwdfd) args = ["/usr/bin/ldappasswd", "-h", self.fqdn, "-ZZ", "-x", "-D", str(DN(('cn', 'Directory Manager'))), "-y", dmpwdfile, "-T", admpwdfile, str(DN(('uid', 'admin'), ('cn', 'users'), ('cn', 'accounts'), self.suffix))] try: env = { 'LDAPTLS_CACERTDIR':os.path.dirname(CACERT), 'LDAPTLS_CACERT':CACERT } ipautil.run(args, env=env) root_logger.debug("ldappasswd done") except ipautil.CalledProcessError, e: print "Unable to set admin password", e root_logger.debug("Unable to set admin password %s" % e) finally: if os.path.isfile(dmpwdfile): os.remove(dmpwdfile) if os.path.isfile(admpwdfile): os.remove(admpwdfile)
def __configure_selinux_for_smbd(self): selinux = False try: if (os.path.exists('/usr/sbin/selinuxenabled')): ipautil.run(["/usr/sbin/selinuxenabled"]) selinux = True except ipautil.CalledProcessError: # selinuxenabled returns 1 if not enabled pass if selinux: # Don't assume all booleans are available sebools = [] for var in self.selinux_booleans: try: (stdout, stderr, returncode) = ipautil.run(["/usr/sbin/getsebool", var]) if stdout and not stderr and returncode == 0: self.backup_state(var, stdout.split()[2]) sebools.append(var) except: pass if sebools: bools = [var + "=true" for var in sebools] args = ["/usr/sbin/setsebool", "-P"] args.extend(bools); try: ipautil.run(args) except: self.print_msg(SELINUX_WARNING % dict(var=','.join(sebools)))
def backup_and_replace_hostname(self, fstore, statestore, hostname): old_hostname = socket.gethostname() try: ipautil.run([paths.BIN_HOSTNAME, hostname]) except ipautil.CalledProcessError, e: print >>sys.stderr, ("Failed to set this machine hostname to " "%s (%s)." % (hostname, str(e)))
def backup_and_replace_hostname(self, fstore, statestore, hostname): old_hostname = socket.gethostname() try: ipautil.run([paths.BIN_HOSTNAME, hostname]) except ipautil.CalledProcessError as e: print(("Failed to set this machine hostname to " "%s (%s)." % (hostname, str(e))), file=sys.stderr) filepath = paths.ETC_HOSTNAME if os.path.exists(filepath): # read old hostname with open(filepath, 'r') as f: for line in f.readlines(): line = line.strip() if not line or line.startswith('#'): # skip comment or empty line continue old_hostname = line break fstore.backup_file(filepath) with open(filepath, 'w') as f: f.write("%s\n" % hostname) os.chmod(filepath, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) os.chown(filepath, 0, 0) self.restore_context(filepath) # store old hostname statestore.backup_state('network', 'hostname', old_hostname)
def _run_getkeytab(self): """ backup and remove old service keytab (if present) and fetch a new one using ipa-getkeytab. This assumes that the service principal is already created in LDAP. By default GSSAPI authentication is used unless: * LDAPI socket is used and effective process UID is 0, then autobind is used by EXTERNAL SASL mech * self.dm_password is not none, then DM credentials are used to fetch keytab """ self.fstore.backup_file(self.keytab) try: os.unlink(self.keytab) except OSError: pass ldap_uri = self.api.env.ldap_uri args = [paths.IPA_GETKEYTAB, '-k', self.keytab, '-p', self.principal, '-H', ldap_uri] nolog = tuple() if ldap_uri.startswith("ldapi://") and os.geteuid() == 0: args.extend(["-Y", "EXTERNAL"]) elif self.dm_password is not None and not self.promote: args.extend( ['-D', 'cn=Directory Manager', '-w', self.dm_password]) nolog += (self.dm_password,) ipautil.run(args, nolog=nolog)
def configure(self, sssd, mkhomedir, statestore, sudo=True): # In the statestore, the following keys are used for the # 'authselect' module: # profile: name of the profile configured pre-installation # features_list: list of features configured pre-installation # mkhomedir: True if installation was called with --mkhomedir # profile and features_list are used when reverting to the # pre-install state cfg = self._parse_authselect_output() if cfg: statestore.backup_state('authselect', 'profile', cfg[0]) statestore.backup_state( 'authselect', 'features_list', " ".join(cfg[1])) else: # cfg = None means that the current conf is not managed by # authselect but by authconfig. # As we are using authselect to configure the host, # it will not be possible to revert to a custom authconfig # configuration later (during uninstall) # Best thing to do will be to use sssd profile at this time logger.warning( "WARNING: The configuration pre-client installation is not " "managed by authselect and cannot be backed up. " "Uninstallation may not be able to revert to the original " "state.") cmd = [paths.AUTHSELECT, "select", "sssd"] if mkhomedir: cmd.append("with-mkhomedir") statestore.backup_state('authselect', 'mkhomedir', True) if sudo: cmd.append("with-sudo") cmd.append("--force") ipautil.run(cmd)
def synconce_ntp(server_fqdn, debug=False): """ Syncs time with specified server using ntpd. Primarily designed to be used before Kerberos setup to get time following the KDC time Returns True if sync was successful """ ntpd = paths.NTPD if not os.path.exists(ntpd): return False # The ntpd command will never exit if it is unable to reach the # server, so timeout after 15 seconds. timeout = 15 tmp_ntp_conf = ipautil.write_tmp_file('server %s' % server_fqdn) args = [paths.BIN_TIMEOUT, str(timeout), ntpd, '-qgc', tmp_ntp_conf.name] if debug: args.append('-d') try: logger.info('Attempting to sync time using ntpd. ' 'Will timeout after %d seconds', timeout) ipautil.run(args) return True except ipautil.CalledProcessError as e: if e.returncode == 124: logger.debug('Process did not complete before timeout') return False
def sync_chrony(): """ This method enables chronyd service on boot and restarts it to reload chrony configuration file /etc/chrony.conf Then it tries to synchronize time with chrony's new or defaut configuration """ # Set the chronyd to start on boot services.knownservices.chronyd.enable() # Restart chronyd services.knownservices.chronyd.restart() sync_attempt_count = 3 # chrony attempt count to sync with configiured servers # each next attempt is tried after 10seconds of timeot # 3 attempts means: if first immidiate attempt fails # there is 10s delay between next attempts args = [paths.CHRONYC, 'waitsync', str(sync_attempt_count), '-d'] try: logger.info('Attempting to sync time with chronyc.') ipautil.run(args) logger.info('Time synchronization was successful.') return True except ipautil.CalledProcessError: logger.warning('Process chronyc waitsync failed to sync time!') logger.warning( "Unable to sync time with chrony server, assuming the time " "is in sync. Please check that 123 UDP port is opened, " "and any time server is on network.") return False
def export_key(args, tmpdir): """Export key and certificate from the NSS DB The private key is encrypted using key wrapping. """ wrapped_key_file = os.path.join(tmpdir, 'wrapped_key') certificate_file = os.path.join(tmpdir, 'certificate') ipautil.run([ paths.PKI, '-d', args.nssdb_path, '-C', args.nssdb_pwdfile, 'ca-authority-key-export', '--wrap-nickname', args.wrap_nickname, '--target-nickname', args.nickname, '-o', wrapped_key_file ]) nssdb = NSSDatabase(args.nssdb_path) nssdb.run_certutil([ '-L', '-n', args.nickname, '-a', '-o', certificate_file, ]) with open(wrapped_key_file, 'rb') as f: wrapped_key = f.read() with open(certificate_file, 'r') as f: certificate = f.read() data = { 'wrapped_key': wrapped_key, 'certificate': certificate } common.json_dump(data, args.exportfile)
def __create_instance(self): pent = pwd.getpwnam(DS_USER) self.backup_state("serverid", self.serverid) self.fstore.backup_file("/etc/sysconfig/dirsrv") self.sub_dict['BASEDC'] = self.realm.split('.')[0].lower() base_txt = ipautil.template_str(BASE_TEMPLATE, self.sub_dict) root_logger.debug(base_txt) target_fname = '/var/lib/dirsrv/boot.ldif' base_fd = open(target_fname, "w") base_fd.write(base_txt) base_fd.close() # Must be readable for dirsrv os.chmod(target_fname, 0440) os.chown(target_fname, pent.pw_uid, pent.pw_gid) inf_txt = ipautil.template_str(INF_TEMPLATE, self.sub_dict) root_logger.debug("writing inf template") inf_fd = ipautil.write_tmp_file(inf_txt) inf_txt = re.sub(r"RootDNPwd=.*\n", "", inf_txt) root_logger.debug(inf_txt) if ipautil.file_exists("/usr/sbin/setup-ds.pl"): args = ["/usr/sbin/setup-ds.pl", "--silent", "--logfile", "-", "-f", inf_fd.name] root_logger.debug("calling setup-ds.pl") else: args = ["/usr/bin/ds_newinst.pl", inf_fd.name] root_logger.debug("calling ds_newinst.pl") try: ipautil.run(args) root_logger.debug("completed creating ds instance") except ipautil.CalledProcessError, e: root_logger.critical("failed to create ds instance %s" % e)
def disable(self, instance_name=""): elements = self.systemd_name.split("@") if instance_name != "" and len(elements) > 1: # Remove instance, we need to do following: # Remove link from /etc/systemd/system/<service>.target.wants/ # <service>@<instance_name>.service # to /lib/systemd/system/<service>@.service srv_tgt = os.path.join(paths.ETC_SYSTEMD_SYSTEM_DIR, self.SYSTEMD_SRV_TARGET % (elements[0])) srv_lnk = os.path.join(srv_tgt, self.service_instance(instance_name)) try: if ipautil.dir_exists(srv_tgt): if os.path.islink(srv_lnk): os.unlink(srv_lnk) ipautil.run([paths.SYSTEMCTL, "--system", "daemon-reload"]) except Exception: pass else: try: ipautil.run([paths.SYSTEMCTL, "disable", self.service_instance(instance_name)]) except ipautil.CalledProcessError: pass
def kinit(self, user, realm, password, ccache_name): # get http service ccache as an armor for FAST to enable OTP authentication armor_principal = str(krb5_format_service_principal_name( 'HTTP', self.api.env.host, realm)) keytab = paths.IPA_KEYTAB armor_name = "%sA_%s" % (krbccache_prefix, user) armor_path = os.path.join(krbccache_dir, armor_name) self.debug('Obtaining armor ccache: principal=%s keytab=%s ccache=%s', armor_principal, keytab, armor_path) try: ipautil.kinit_keytab(armor_principal, paths.IPA_KEYTAB, armor_path) except gssapi.exceptions.GSSError as e: raise CCacheError(str(e)) # Format the user as a kerberos principal principal = krb5_format_principal_name(user, realm) try: ipautil.kinit_password(principal, password, ccache_name, armor_ccache_name=armor_path) self.debug('Cleanup the armor ccache') ipautil.run( [paths.KDESTROY, '-A', '-c', armor_path], env={'KRB5CCNAME': armor_path}, raiseonerr=False) except RuntimeError as e: if ('kinit: Cannot read password while ' 'getting initial credentials') in str(e): raise PasswordExpired(principal=principal, message=unicode(e)) raise InvalidSessionPassword(principal=principal, message=unicode(e))
def map_Guests_to_nobody(): env = {'LC_ALL': 'C'} args = [paths.NET, '-s', '/dev/null', 'groupmap', 'add', 'sid=S-1-5-32-546', 'unixgroup=nobody', 'type=builtin'] logger.debug("Map BUILTIN\\Guests to a group 'nobody'") ipautil.run(args, env=env, raiseonerr=False, capture_error=True)
def reload_systemwide_ca_store(self): try: ipautil.run([paths.UPDATE_CA_TRUST]) except CalledProcessError, e: root_logger.error( "Could not update systemwide CA trust database: %s", e) return False
def remove_httpd_ccache(self): # Clean up existing ccache # Make sure that empty env is passed to avoid passing KRB5CCNAME from # current env ipautil.run( [paths.KDESTROY, '-A'], runas=self.service_user, raiseonerr=False, env={})
def import_key(self, value): v = json_decode(value) data = b64decode(v['pkcs12 data']) password = v['export password'] try: _fd, tmpdata = tempfile.mkstemp(dir=paths.TMP) with open(tmpdata, 'w') as f: f.write(data) # get the certificate from the file ipautil.run([paths.OPENSSL, "pkcs12", "-in", tmpdata, "-clcerts", "-nokeys", "-out", self.certfile, "-passin", "pass:{pwd}".format(pwd=password)], nolog=(password, )) if self.keyfile is not None: # get the private key from the file ipautil.run([paths.OPENSSL, "pkcs12", "-in", tmpdata, "-nocerts", "-nodes", "-out", self.keyfile, "-passin", "pass:{pwd}".format(pwd=password)], nolog=(password, )) finally: os.remove(tmpdata)
def hsm_replica_sync(self): """Download keys from LDAP to local HSM.""" if self.ismaster: return if not self.init_done: return ipautil.run([paths.IPA_DNSKEYSYNCD_REPLICA])
def setup_firefox_extension(self, realm, domain): """Set up the signed browser configuration extension """ target_fname = paths.KRB_JS sub_dict = dict(REALM=realm, DOMAIN=domain) db = certs.CertDB(realm) with open(db.passwd_fname) as pwdfile: pwd = pwdfile.read() ipautil.copy_template_file(ipautil.SHARE_DIR + "krb.js.template", target_fname, sub_dict) os.chmod(target_fname, 0o644) # Setup extension tmpdir = tempfile.mkdtemp(prefix="tmp-") extdir = tmpdir + "/ext" target_fname = paths.KERBEROSAUTH_XPI shutil.copytree(paths.FFEXTENSION, extdir) if db.has_nickname('Signing-Cert'): db.run_signtool(["-k", "Signing-Cert", "-p", pwd, "-X", "-Z", target_fname, extdir]) else: root_logger.warning('Object-signing certificate was not found. ' 'Creating unsigned Firefox configuration extension.') filenames = os.listdir(extdir) ipautil.run([paths.ZIP, '-r', target_fname] + filenames, cwd=extdir) shutil.rmtree(tmpdir) os.chmod(target_fname, 0o644)
def __create_instance(self): pent = pwd.getpwnam(DS_USER) self.backup_state("serverid", self.serverid) self.fstore.backup_file(paths.SYSCONFIG_DIRSRV) self.sub_dict['BASEDC'] = self.realm.split('.')[0].lower() base_txt = ipautil.template_str(BASE_TEMPLATE, self.sub_dict) root_logger.debug(base_txt) target_fname = paths.DIRSRV_BOOT_LDIF base_fd = open(target_fname, "w") base_fd.write(base_txt) base_fd.close() # Must be readable for dirsrv os.chmod(target_fname, 0440) os.chown(target_fname, pent.pw_uid, pent.pw_gid) inf_txt = ipautil.template_str(INF_TEMPLATE, self.sub_dict) root_logger.debug("writing inf template") inf_fd = ipautil.write_tmp_file(inf_txt) inf_txt = re.sub(r"RootDNPwd=.*\n", "", inf_txt) root_logger.debug(inf_txt) if ipautil.file_exists(paths.SETUP_DS_PL): args = [paths.SETUP_DS_PL, "--silent", "--logfile", "-", "-f", inf_fd.name] root_logger.debug("calling setup-ds.pl") else: args = [paths.DS_NEWINST_PL, inf_fd.name] root_logger.debug("calling ds_newinst.pl") try: ipautil.run(args) root_logger.debug("completed creating ds instance") except ipautil.CalledProcessError, e: root_logger.critical("failed to create ds instance %s" % e)
def export_key(self): tdir = tempfile.mkdtemp(dir=paths.TMP) try: nsspwfile = os.path.join(tdir, 'nsspwfile') with open(nsspwfile, 'w+') as f: f.write(self.nssdb_password) wrapped_key_file = os.path.join(tdir, 'wrapped_key') certificate_file = os.path.join(tdir, 'certificate') ipautil.run([ paths.PKI, '-d', self.nssdb_path, '-C', nsspwfile, 'ca-authority-key-export', '--wrap-nickname', self.wrap_nick, '--target-nickname', self.target_nick, '-o', wrapped_key_file]) ipautil.run([ paths.CERTUTIL, '-d', self.nssdb_path, '-L', '-n', self.target_nick, '-a', '-o', certificate_file]) with open(wrapped_key_file, 'r') as f: wrapped_key = f.read() with open(certificate_file, 'r') as f: certificate = f.read() finally: shutil.rmtree(tdir) return json_encode({ 'wrapped_key': b64encode(wrapped_key), 'certificate': certificate})
def verify_kdc_cert_validity(kdc_cert, ca_certs, realm): with NamedTemporaryFile() as kdc_file, NamedTemporaryFile() as ca_file: kdc_file.write(kdc_cert.public_bytes(x509.Encoding.PEM)) kdc_file.flush() x509.write_certificate_list(ca_certs, ca_file.name) ca_file.flush() try: ipautil.run( [paths.OPENSSL, 'verify', '-CAfile', ca_file.name, kdc_file.name], capture_output=True) except ipautil.CalledProcessError as e: raise ValueError(e.output) try: eku = kdc_cert.extensions.get_extension_for_class( cryptography.x509.ExtendedKeyUsage) list(eku.value).index( cryptography.x509.ObjectIdentifier(x509.EKU_PKINIT_KDC)) except (cryptography.x509.ExtensionNotFound, ValueError): raise ValueError("invalid for a KDC") principal = str(Principal(['krbtgt', realm], realm)) gns = x509.process_othernames(kdc_cert.san_general_names) for gn in gns: if isinstance(gn, x509.KRB5PrincipalName) and gn.name == principal: break else: raise ValueError("invalid for realm %s" % realm)
def export_key(self): tdir = tempfile.mkdtemp(dir=paths.TMP) try: wrapped_key_file = os.path.join(tdir, 'wrapped_key') certificate_file = os.path.join(tdir, 'certificate') ipautil.run([ paths.PKI, '-d', self.nssdb_path, '-C', self.nssdb_pwdfile, 'ca-authority-key-export', '--wrap-nickname', self.wrap_nick, '--target-nickname', self.target_nick, '-o', wrapped_key_file]) nssdb = NSSDatabase(self.nssdb_path) nssdb.run_certutil([ '-L', '-n', self.target_nick, '-a', '-o', certificate_file, ]) with open(wrapped_key_file, 'rb') as f: wrapped_key = f.read() with open(certificate_file, 'r') as f: certificate = f.read() finally: shutil.rmtree(tdir) return json_encode({ 'wrapped_key': b64encode(wrapped_key).decode('ascii'), 'certificate': certificate})
def spawn_instance(self, cfg_file, nolog_list=None): """ Create and configure a new Dogtag instance using pkispawn. Passes in a configuration file with IPA-specific parameters. """ subsystem = self.subsystem # Define the things we don't want logged if nolog_list is None: nolog_list = [] nolog = tuple(nolog_list) + (self.admin_password, self.dm_password) args = [paths.PKISPAWN, "-s", subsystem, "-f", cfg_file] with open(cfg_file) as f: self.log.debug( 'Contents of pkispawn configuration file (%s):\n%s', cfg_file, ipautil.nolog_replace(f.read(), nolog)) try: ipautil.run(args, nolog=nolog) except ipautil.CalledProcessError as e: self.handle_setup_error(e)
def run_getkeytab(self, ldap_uri, keytab, principal, retrieve=False): """ retrieve service keytab using ipa-getkeytab. This assumes that the service principal is already created in LDAP. By default GSSAPI authentication is used unless: * LDAPI socket is used and effective process UID is 0, then autobind is used by EXTERNAL SASL mech * self.dm_password is not none, then DM credentials are used to fetch keytab """ args = [paths.IPA_GETKEYTAB, '-k', keytab, '-p', principal, '-H', ldap_uri] nolog = tuple() if ldap_uri.startswith("ldapi://") and os.geteuid() == 0: args.extend(["-Y", "EXTERNAL"]) elif self.dm_password is not None and not self.promote: args.extend( ['-D', 'cn=Directory Manager', '-w', self.dm_password]) nolog += (self.dm_password,) if retrieve: args.extend(['-r']) ipautil.run(args, nolog=nolog)
def tune_nofile_platform(self, num=8192, fstore=None): """ Increase the number of files descriptors available to directory server from the default 1024 to 8192. This will allow to support a greater number of clients out of the box. This is a part of the implementation that is systemd-specific. Returns False if the setting of the nofile limit needs to be skipped. """ if os.path.exists(paths.SYSCONFIG_DIRSRV_SYSTEMD): # We need to enable LimitNOFILE=8192 in the [email protected] # Since 389-ds-base-1.2.10-0.8.a7 the configuration of the # service parameters is performed via # /etc/sysconfig/dirsrv.systemd file which is imported by systemd # into [email protected] unit replacevars = {'LimitNOFILE': str(num)} ipautil.inifile_replace_variables(paths.SYSCONFIG_DIRSRV_SYSTEMD, 'service', replacevars=replacevars) tasks.restore_context(paths.SYSCONFIG_DIRSRV_SYSTEMD) ipautil.run(["/bin/systemctl", "--system", "daemon-reload"], raiseonerr=False) return True
def kinit_password(principal, password, ccache_name, config=None, armor_ccache_name=None, canonicalize=False, enterprise=False): """ perform interactive kinit as principal using password. If using FAST for web-based authentication, use armor_ccache_path to specify http service ccache. """ root_logger.debug("Initializing principal %s using password" % principal) args = [paths.KINIT, principal, '-c', ccache_name] if armor_ccache_name is not None: root_logger.debug("Using armor ccache %s for FAST webauth" % armor_ccache_name) args.extend(['-T', armor_ccache_name]) if canonicalize: root_logger.debug("Requesting principal canonicalization") args.append('-C') if enterprise: root_logger.debug("Using enterprise principal") args.append('-E') env = {'LC_ALL': 'C'} if config is not None: env['KRB5_CONFIG'] = config # this workaround enables us to capture stderr and put it # into the raised exception in case of unsuccessful authentication result = run(args, stdin=password, env=env, raiseonerr=False, capture_error=True) if result.returncode: raise RuntimeError(result.error_output)
def db2bak(self, instance, online=True): ''' Create a BAK backup of the data and changelog in this instance. If executed online create a task and wait for it to complete. ''' logger.info('Backing up %s', instance) cn = time.strftime('backup_%Y_%m_%d_%H_%M_%S') dn = DN(('cn', cn), ('cn', 'backup'), ('cn', 'tasks'), ('cn', 'config')) bakdir = os.path.join(paths.SLAPD_INSTANCE_BACKUP_DIR_TEMPLATE % (instance, instance)) if online: conn = self.get_connection() ent = conn.make_entry( dn, { 'objectClass': ['top', 'extensibleObject'], 'cn': [cn], 'nsInstance': ['userRoot'], 'nsArchiveDir': [bakdir], 'nsDatabaseType': ['ldbm database'], }) try: conn.add_entry(ent) except Exception as e: raise admintool.ScriptError( 'Unable to to add backup task: %s' % e) logger.info("Waiting for BAK to finish") wait_for_task(conn, dn) else: args = [paths.DB2BAK, bakdir, '-Z', instance] result = run(args, raiseonerr=False) if result.returncode != 0: logger.critical('db2bak failed: %s', result.error_log) shutil.move(bakdir, self.dir)
def is_running(self, instance_name="", wait=True): instance = self.service_instance(instance_name, 'is-active') while True: try: result = ipautil.run([paths.SYSTEMCTL, "is-active", instance], capture_output=True) except ipautil.CalledProcessError as e: if e.returncode == 3 and 'activating' in str(e.output): time.sleep(SERVICE_POLL_INTERVAL) continue return False else: # activating if result.returncode == 3 and 'activating' in result.output: time.sleep(SERVICE_POLL_INTERVAL) continue # active if result.returncode == 0: return True # not active return False
def decrypt_file(tmpdir, filename): source = filename (dest, ext) = os.path.splitext(filename) if ext != '.gpg': raise admintool.ScriptError('Trying to decrypt a non-gpg file') dest = os.path.basename(dest) dest = os.path.join(tmpdir, dest) args = [ paths.GPG2, '--batch', '--output', dest, '--decrypt', source, ] result = run(args, raiseonerr=False) if result.returncode != 0: raise admintool.ScriptError('gpg failed: %s' % result.error_log) return dest
def encrypt_file(filename, remove_original=True): source = filename dest = filename + '.gpg' args = [ paths.GPG2, '--batch', '--default-recipient-self', '--output', dest, '--encrypt', source, ] result = run(args, raiseonerr=False) if result.returncode != 0: raise admintool.ScriptError('gpg failed: %s' % result.error_log) if remove_original: os.unlink(source) return dest
def restore_default_conf(self): ''' Restore paths.IPA_DEFAULT_CONF to temporary directory. Primary purpose of this method is to get cofiguration for api finalization when restoring ipa after uninstall. ''' cwd = os.getcwd() os.chdir(self.dir) args = ['tar', '--xattrs', '--selinux', '-xzf', os.path.join(self.dir, 'files.tar'), paths.IPA_DEFAULT_CONF[1:], ] result = run(args, raiseonerr=False) if result.returncode != 0: logger.critical('Restoring %s failed: %s', paths.IPA_DEFAULT_CONF, result.error_log) os.chdir(cwd)
def check_inst(): """ We now allow mod_ssl to be installed so don't automatically disable it. However it can't share the same listen port as mod_nss, so check for that. Returns True if something other than mod_nss is listening on 443. False otherwise. """ (stdout, stderr, rc) = ipautil.run(['/usr/sbin/httpd', '-t', '-D', 'DUMP_VHOSTS'], raiseonerr=False) if rc == 0: lines = stdout.split('\n') for line in lines: if ':443 ' in line: if not 'nss.conf' in line: print "Apache is already configured with a listener on port 443:" print line print "IPA requires that mod_nss be used for port 443." return True return False
def run_ods_manager(self, params, **kwargs): """Run OpenDNSSEC manager command (ksmutil, enforcer) :param params: parameter for ODS command :param kwargs: additional arguments for ipautil.run() :return: result from ipautil.run() """ assert params[0] != 'setup' if paths.ODS_KSMUTIL is not None: # OpenDNSSEC 1.4 cmd = [paths.ODS_KSMUTIL] else: # OpenDNSSEC 2.x cmd = [paths.ODS_ENFORCER] cmd.extend(params) # run commands as ODS user if os.geteuid() == 0: kwargs['runas'] = constants.ODS_USER return ipautil.run(cmd, **kwargs)
def run_handler(self, extra_args=(), stdin=None): """Run handler script to export / import key material """ args = [self.command] args.extend(extra_args) kwargs = dict( runas=self.runas, encoding='utf-8', ) if stdin: args.extend(['--import', '-']) kwargs.update(stdin=stdin) else: args.extend(['--export', '-']) kwargs.update(capture_output=True) result = ipautil.run(args, **kwargs) if stdin is None: return result.output else: return None
def file_restore(self, nologs=False): ''' Restore all the files in the tarball. This MUST be done offline because we directly backup the 389-ds databases. ''' logger.info("Restoring files") cwd = os.getcwd() os.chdir('/') args = [ 'tar', '--xattrs', '--selinux', '-xzf', os.path.join(self.dir, 'files.tar') ] if nologs: args.append('--exclude') args.append('var/log') result = run(args, raiseonerr=False) if result.returncode != 0: logger.critical('Restoring files failed: %s', result.error_log) os.chdir(cwd)
def file_restore(self, nologs=False): ''' Restore all the files in the tarball. This MUST be done offline because we directly backup the 389-ds databases. ''' self.log.info("Restoring files") cwd = os.getcwd() os.chdir('/') args = [ 'tar', '--xattrs', '--selinux', '-xzf', os.path.join(self.dir, 'files.tar') ] if nologs: args.append('--exclude') args.append('var/log') (stdout, stderr, rc) = run(args, raiseonerr=False) if rc != 0: self.log.critical('Restoring files failed: %s', stderr) os.chdir(cwd)
def check(self): rval = constants.SUCCESS if not os.path.exists(paths.FIPS_MODE_SETUP): fips = "missing {}".format(paths.FIPS_MODE_SETUP) logger.debug('%s is not installed, skipping', paths.FIPS_MODE_SETUP) else: try: result = ipautil.run( [paths.FIPS_MODE_SETUP, '--is-enabled'], capture_output=True, raiseonerr=False, ) except Exception as e: logger.debug('fips-mode-setup failed: %s', e) fips = "failed to check" rval = constants.ERROR else: logger.debug(result.raw_output.decode('utf-8')) if result.returncode == 0: fips = "enabled" elif result.returncode == 1: fips = "inconsistent" elif result.returncode == 2: fips = "disabled" else: fips = "unknown" yield Result( self, rval, fqdn=socket.getfqdn(), fips=fips, ipa_version=VERSION, ipa_api_version=API_VERSION, )
def stop_tracking(secdir, request_id=None, nickname=None): """ Stop tracking the current request using either the request_id or nickname. This assumes that the certmonger service is running. """ if request_id is None and nickname is None: raise RuntimeError('Both request_id and nickname are missing.') if nickname: # Using the nickname find the certmonger request_id criteria = (('cert_storage_location', os.path.abspath(secdir), NPATH), ('cert_nickname', nickname, None)) try: request_id = get_request_id(criteria) if request_id is None: return ('', '', 0) except RuntimeError: # This means that multiple requests matched, skip it for now # Fall back to trying to stop tracking using nickname pass args = [ '/usr/bin/getcert', 'stop-tracking', ] if request_id: args.append('-i') args.append(request_id) else: args.append('-n') args.append(nickname) args.append('-d') args.append(os.path.abspath(secdir)) (stdout, stderr, returncode) = ipautil.run(args) return (stdout, stderr, returncode)
def wait_until_running(self): root_logger.debug('Waiting until the CA is running') timeout = float(api.env.startup_timeout) op_timeout = time.time() + timeout while time.time() < op_timeout: try: # FIXME https://fedorahosted.org/freeipa/ticket/4716 # workaround # # status = dogtag.ca_status(use_proxy=use_proxy) # port = 8443 url = "https://%(host_port)s%(path)s" % { "host_port": ipautil.format_netloc(api.env.ca_host, port), "path": "/ca/admin/ca/getStatus" } args = [ paths.BIN_CURL, '-o', '-', '--connect-timeout', '30', '-k', url ] result = ipautil.run(args, capture_output=True) status = dogtag._parse_ca_status(result.output) # end of workaround except Exception as e: status = 'check interrupted due to error: %s' % e root_logger.debug('The CA status is: %s' % status) if status == 'running': break root_logger.debug('Waiting for CA to start...') time.sleep(1) else: raise RuntimeError('CA did not start in %ss' % timeout)
def request_cert(nssdb, nickname, subject, principal, passwd_fname=None): """ Execute certmonger to request a server certificate """ args = [ '/usr/bin/ipa-getcert', 'request', '-d', nssdb, '-n', nickname, '-N', subject, '-K', principal, ] if passwd_fname: args.append('-p') args.append(os.path.abspath(passwd_fname)) (stdout, stderr, returncode) = ipautil.run(args) # FIXME: should be some error handling around this m = re.match('New signing request "(\d+)" added', stdout) request_id = m.group(1) return request_id
def encrypt_file(filename, keyring, remove_original=True): source = filename dest = filename + '.gpg' args = [paths.GPG, '--batch', '--default-recipient-self', '-o', dest] if keyring is not None: args.append('--no-default-keyring') args.append('--keyring') args.append(keyring + '.pub') args.append('--secret-keyring') args.append(keyring + '.sec') args.append('-e') args.append(source) result = run(args, raiseonerr=False) if result.returncode != 0: raise admintool.ScriptError('gpg failed: %s' % result.error_log) if remove_original: os.unlink(source) return dest
def copy_key(self, keytab, keyentry): # keyentry.key is a hex value of the actual key # prefixed with 0x, as produced by klist -K -k. # However, ktutil accepts hex value without 0x, so # we should strip first two characters. stdin = dedent("""\ rkt {keytab} addent -key -p {principal} -k {kvno} -e {etype} {key} wkt {keytab} """).format(keytab=keytab, principal=keyentry.principal, kvno=keyentry.kvno, etype=keyentry.etype, key=keyentry.key[2:]) result = ipautil.run([paths.KTUTIL], stdin=stdin, raiseonerr=False, umask=0o077, nolog_output=True) if result.returncode != 0: logger.warning('Unable to update %s with new keys', keytab)
def install_pem_from_p12(p12_fname, p12_passwd, pem_fname): pwd = ipautil.write_tmp_file(p12_passwd) ipautil.run([ paths.OPENSSL, "pkcs12", "-nokeys", "-clcerts", "-in", p12_fname, "-out", pem_fname, "-passin", "file:" + pwd.name ])
def export_pem_p12(pkcs12_fname, pkcs12_pwd_fname, nickname, pem_fname): ipautil.run([ paths.OPENSSL, "pkcs12", "-export", "-name", nickname, "-in", pem_fname, "-out", pkcs12_fname, "-passout", "file:" + pkcs12_pwd_fname ])
def install(installer): options = installer fstore = installer._fstore sstore = installer._sstore dirsrv_pkcs12_info = installer._dirsrv_pkcs12_info http_pkcs12_info = installer._http_pkcs12_info pkinit_pkcs12_info = installer._pkinit_pkcs12_info http_ca_cert = installer._ca_cert realm_name = options.realm_name domain_name = options.domain_name dm_password = options.dm_password master_password = options.master_password admin_password = options.admin_password host_name = options.host_name ip_addresses = options.ip_addresses setup_ca = options.setup_ca # Installation has started. No IPA sysrestore items are restored in case of # failure to enable root cause investigation installer._installation_cleanup = False if installer.interactive: print("") print("The following operations may take some minutes to complete.") print("Please wait until the prompt is returned.") print("") # set hostname (transient and static) if user instructed us to do so if options._host_name_overridden: tasks.backup_hostname(fstore, sstore) tasks.set_hostname(host_name) if installer._update_hosts_file: update_hosts_file(ip_addresses, host_name, fstore) # Make sure tmpfiles dir exist before installing components tasks.create_tmpfiles_dirs() # Create a directory server instance if not options.external_cert_files: # Configure ntpd if not options.no_ntp: ipaclient.install.ntpconf.force_ntpd(sstore) ntp = ntpinstance.NTPInstance(fstore) if not ntp.is_configured(): ntp.create_instance() if options.dirsrv_cert_files: ds = dsinstance.DsInstance(fstore=fstore, domainlevel=options.domainlevel, config_ldif=options.dirsrv_config_file) installer._ds = ds ds.create_instance(realm_name, host_name, domain_name, dm_password, dirsrv_pkcs12_info, idstart=options.idstart, idmax=options.idmax, subject_base=options.subject_base, ca_subject=options.ca_subject, hbac_allow=not options.no_hbac_allow) else: ds = dsinstance.DsInstance(fstore=fstore, domainlevel=options.domainlevel, config_ldif=options.dirsrv_config_file) installer._ds = ds ds.create_instance(realm_name, host_name, domain_name, dm_password, idstart=options.idstart, idmax=options.idmax, subject_base=options.subject_base, ca_subject=options.ca_subject, hbac_allow=not options.no_hbac_allow) ntpinstance.ntp_ldap_enable(host_name, ds.suffix, realm_name) else: api.Backend.ldap2.connect() ds = dsinstance.DsInstance(fstore=fstore, domainlevel=options.domainlevel) installer._ds = ds ds.init_info(realm_name, host_name, domain_name, dm_password, options.subject_base, options.ca_subject, 1101, 1100, None) krb = krbinstance.KrbInstance(fstore) krb.create_instance(realm_name, host_name, domain_name, dm_password, master_password, setup_pkinit=not options.no_pkinit, pkcs12_info=pkinit_pkcs12_info, subject_base=options.subject_base) if setup_ca: if not options.external_cert_files and options.external_ca: # stage 1 of external CA installation options.realm_name = realm_name options.domain_name = domain_name options.master_password = master_password options.dm_password = dm_password options.admin_password = admin_password options.host_name = host_name options.reverse_zones = dns.reverse_zones cache_vars = { n: options.__dict__[n] for o, n in installer.knobs() if n in options.__dict__ } write_cache(cache_vars) ca.install_step_0(False, None, options) else: # Put the CA cert where other instances expect it x509.write_certificate(http_ca_cert, paths.IPA_CA_CRT) os.chmod(paths.IPA_CA_CRT, 0o444) # we now need to enable ssl on the ds ds.enable_ssl() if setup_ca: ca.install_step_1(False, None, options) otpd = otpdinstance.OtpdInstance() otpd.create_instance('OTPD', host_name, ipautil.realm_to_suffix(realm_name)) custodia = custodiainstance.CustodiaInstance(host_name, realm_name) custodia.create_instance() # Create a HTTP instance http = httpinstance.HTTPInstance(fstore) if options.http_cert_files: http.create_instance(realm_name, host_name, domain_name, pkcs12_info=http_pkcs12_info, subject_base=options.subject_base, auto_redirect=not options.no_ui_redirect, ca_is_configured=setup_ca) else: http.create_instance(realm_name, host_name, domain_name, subject_base=options.subject_base, auto_redirect=not options.no_ui_redirect, ca_is_configured=setup_ca) tasks.restore_context(paths.CACHE_IPA_SESSIONS) ca.set_subject_base_in_config(options.subject_base) # configure PKINIT now that all required services are in place krb.enable_ssl() # Apply any LDAP updates. Needs to be done after the configuration file # is created. DS is restarted in the process. service.print_msg("Applying LDAP updates") ds.apply_updates() # Restart krb after configurations have been changed service.print_msg("Restarting the KDC") krb.restart() if options.setup_kra: kra.install(api, None, options) if options.setup_dns: dns.install(False, False, options) else: # Create a BIND instance bind = bindinstance.BindInstance(fstore) bind.setup(host_name, ip_addresses, realm_name, domain_name, (), 'first', (), zonemgr=options.zonemgr, no_dnssec_validation=options.no_dnssec_validation) bind.create_file_with_system_records() if options.setup_adtrust: adtrust.install(False, options, fstore, api) # Set the admin user kerberos password ds.change_admin_password(admin_password) # Call client install script service.print_msg("Configuring client side components") try: args = [ paths.IPA_CLIENT_INSTALL, "--on-master", "--unattended", "--domain", domain_name, "--server", host_name, "--realm", realm_name, "--hostname", host_name ] if options.no_dns_sshfp: args.append("--no-dns-sshfp") if options.ssh_trust_dns: args.append("--ssh-trust-dns") if options.no_ssh: args.append("--no-ssh") if options.no_sshd: args.append("--no-sshd") if options.mkhomedir: args.append("--mkhomedir") run(args, redirect_output=True) print() except Exception: raise ScriptError("Configuration of client side components failed!") # Make sure the files we crated in /var/run are recreated at startup tasks.configure_tmpfiles() # Everything installed properly, activate ipa service. services.knownservices.ipa.enable() print("=======================================" "=======================================") print("Setup complete") print("") print("Next steps:") print("\t1. You must make sure these network ports are open:") print("\t\tTCP Ports:") print("\t\t * 80, 443: HTTP/HTTPS") print("\t\t * 389, 636: LDAP/LDAPS") print("\t\t * 88, 464: kerberos") if options.setup_dns: print("\t\t * 53: bind") print("\t\tUDP Ports:") print("\t\t * 88, 464: kerberos") if options.setup_dns: print("\t\t * 53: bind") if not options.no_ntp: print("\t\t * 123: ntp") print("") print("\t2. You can now obtain a kerberos ticket using the command: " "'kinit admin'") print("\t This ticket will allow you to use the IPA tools (e.g., ipa " "user-add)") print("\t and the web user interface.") if not services.knownservices.ntpd.is_running(): print("\t3. Kerberos requires time synchronization between clients") print("\t and servers for correct operation. You should consider " "enabling ntpd.") print("") if setup_ca: print(("Be sure to back up the CA certificates stored in " + paths.CACERT_P12)) print("These files are required to create replicas. The password for " "these") print("files is the Directory Manager password") else: print( "In order for Firefox autoconfiguration to work you will need to") print("use a SSL signing certificate. See the IPA documentation for " "more details.") if ipautil.file_exists(paths.ROOT_IPA_CACHE): os.remove(paths.ROOT_IPA_CACHE)
def ldif2db(self, instance, backend, online=True): ''' Restore a LDIF backup of the data in this instance. If executed online create a task and wait for it to complete. ''' logger.info('Restoring from %s in %s', backend, instance) cn = time.strftime('import_%Y_%m_%d_%H_%M_%S') dn = DN(('cn', cn), ('cn', 'import'), ('cn', 'tasks'), ('cn', 'config')) ldifdir = paths.SLAPD_INSTANCE_LDIF_DIR_TEMPLATE % instance ldifname = '%s-%s.ldif' % (instance, backend) ldiffile = os.path.join(ldifdir, ldifname) srcldiffile = os.path.join(self.dir, ldifname) if not os.path.exists(ldifdir): pent = pwd.getpwnam(constants.DS_USER) os.mkdir(ldifdir) os.chmod(ldifdir, 0o770) os.chown(ldifdir, pent.pw_uid, pent.pw_gid) ipautil.backup_file(ldiffile) with open(ldiffile, 'w') as out_file: ldif_writer = ldif.LDIFWriter(out_file) with open(srcldiffile, 'rb') as in_file: ldif_parser = RemoveRUVParser(in_file, ldif_writer) ldif_parser.parse() # Make sure the modified ldiffile is owned by DS_USER pent = pwd.getpwnam(constants.DS_USER) os.chown(ldiffile, pent.pw_uid, pent.pw_gid) if online: conn = self.get_connection() ent = conn.make_entry( dn, { 'objectClass': ['top', 'extensibleObject'], 'cn': [cn], 'nsFilename': [ldiffile], 'nsUseOneFile': ['true'], } ) ent['nsInstance'] = [backend] try: conn.add_entry(ent) except Exception as e: logger.error("Unable to bind to LDAP server: %s", e) return logger.info("Waiting for LDIF to finish") wait_for_task(conn, dn) else: try: os.makedirs(paths.VAR_LOG_DIRSRV_INSTANCE_TEMPLATE % instance) except OSError as e: pass args = [paths.LDIF2DB, '-Z', instance, '-i', ldiffile, '-n', backend] result = run(args, raiseonerr=False) if result.returncode != 0: logger.critical("ldif2db failed: %s", result.error_log)
def run_pk12util(self, args, stdin=None, **kwargs): new_args = [ paths.PK12UTIL, "-d", '{}:{}'.format(self.dbtype, self.secdir) ] new_args.extend(args) return ipautil.run(new_args, stdin, **kwargs)
def uninstall(installer): fstore = installer._fstore sstore = installer._sstore rv = 0 # further steps assumes that temporary directories exists so rather # ensure they are created tasks.create_tmpfiles_dirs() print("Shutting down all IPA services") try: services.knownservices.ipa.stop() except Exception: # Fallback to direct ipactl stop only if system command fails try: run([paths.IPACTL, "stop"], raiseonerr=False) except Exception: pass ntpinstance.NTPInstance(fstore).uninstall() kra.uninstall() ca.uninstall() dns.uninstall() httpinstance.HTTPInstance(fstore).uninstall() krbinstance.KrbInstance(fstore).uninstall() dsinstance.DsInstance(fstore=fstore).uninstall() if _server_trust_ad_installed: adtrustinstance.ADTRUSTInstance(fstore).uninstall() custodiainstance.CustodiaInstance().uninstall() otpdinstance.OtpdInstance().uninstall() tasks.restore_hostname(fstore, sstore) fstore.restore_all_files() try: os.remove(paths.ROOT_IPA_CACHE) except Exception: pass try: os.remove(paths.ROOT_IPA_CSR) except Exception: pass # ipa-client-install removes /etc/ipa/default.conf sstore._load() ipaclient.install.ntpconf.restore_forced_ntpd(sstore) # Clean up group_exists (unused since IPA 2.2, not being set since 4.1) sstore.restore_state("install", "group_exists") services.knownservices.ipa.disable() # remove upgrade state file sysupgrade.remove_upgrade_file() if fstore.has_files(): root_logger.error('Some files have not been restored, see ' '%s/sysrestore.index' % SYSRESTORE_DIR_PATH) has_state = False for module in IPA_MODULES: # from installutils if sstore.has_state(module): root_logger.error('Some installation state for %s has not been ' 'restored, see %s/sysrestore.state' % (module, SYSRESTORE_DIR_PATH)) has_state = True rv = 1 if has_state: root_logger.error('Some installation state has not been restored.\n' 'This may cause re-installation to fail.\n' 'It should be safe to remove %s/sysrestore.state ' 'but it may\n' 'mean your system hasn\'t be restored to its ' 'pre-installation state.' % SYSRESTORE_DIR_PATH) # Note that this name will be wrong after the first uninstall. dirname = dsinstance.config_dirname( installutils.realm_to_serverid(api.env.realm)) dirs = [dirname, paths.PKI_TOMCAT_ALIAS_DIR, paths.HTTPD_ALIAS_DIR] ids = certmonger.check_state(dirs) if ids: root_logger.error('Some certificates may still be tracked by ' 'certmonger.\n' 'This will cause re-installation to fail.\n' 'Start the certmonger service and list the ' 'certificates being tracked\n' ' # getcert list\n' 'These may be untracked by executing\n' ' # getcert stop-tracking -i <request_id>\n' 'for each id in: %s' % ', '.join(ids)) # Remove the cert renewal lock file try: os.remove(paths.IPA_RENEWAL_LOCK) except OSError as e: if e.errno != errno.ENOENT: root_logger.warning("Failed to remove file %s: %s", paths.IPA_RENEWAL_LOCK, e) print("Removing IPA client configuration") try: result = run([ paths.IPA_CLIENT_INSTALL, "--on-master", "--unattended", "--uninstall" ], raiseonerr=False, redirect_output=True) if result.returncode not in [0, 2]: raise RuntimeError("Failed to configure the client") except Exception: rv = 1 print("Uninstall of client side components failed!") sys.exit(rv)
def install(installer): options = installer fstore = installer._fstore sstore = installer._sstore dirsrv_pkcs12_info = installer._dirsrv_pkcs12_info http_pkcs12_info = installer._http_pkcs12_info pkinit_pkcs12_info = installer._pkinit_pkcs12_info http_ca_cert = installer._ca_cert realm_name = options.realm_name domain_name = options.domain_name dm_password = options.dm_password master_password = options.master_password admin_password = options.admin_password host_name = options.host_name ip_addresses = options.ip_addresses setup_ca = options.setup_ca # Installation has started. No IPA sysrestore items are restored in case of # failure to enable root cause investigation installer._installation_cleanup = False if installer.interactive: print("") print("The following operations may take some minutes to complete.") print("Please wait until the prompt is returned.") print("") # set hostname (transient and static) if user instructed us to do so if options._host_name_overridden: tasks.backup_hostname(fstore, sstore) tasks.set_hostname(host_name) if installer._update_hosts_file: update_hosts_file(ip_addresses, host_name, fstore) if tasks.configure_pkcs11_modules(fstore): print("Disabled p11-kit-proxy") # Create a directory server instance if not options.external_cert_files: # We have to sync time before certificate handling on master. # As chrony configuration is moved from client here, unconfiguration of # chrony will be handled here in uninstall() method as well by invoking # the ipa-server-install --uninstall if not options.no_ntp and not sync_time( options.ntp_servers, options.ntp_pool, fstore, sstore): print("Warning: IPA was unable to sync time with chrony!") print(" Time synchronization is required for IPA " "to work correctly") if options.dirsrv_cert_files: ds = dsinstance.DsInstance(fstore=fstore, domainlevel=options.domainlevel, config_ldif=options.dirsrv_config_file) installer._ds = ds ds.create_instance(realm_name, host_name, domain_name, dm_password, dirsrv_pkcs12_info, idstart=options.idstart, idmax=options.idmax, subject_base=options.subject_base, ca_subject=options.ca_subject, hbac_allow=not options.no_hbac_allow, setup_pkinit=not options.no_pkinit) else: ds = dsinstance.DsInstance(fstore=fstore, domainlevel=options.domainlevel, config_ldif=options.dirsrv_config_file) installer._ds = ds ds.create_instance(realm_name, host_name, domain_name, dm_password, idstart=options.idstart, idmax=options.idmax, subject_base=options.subject_base, ca_subject=options.ca_subject, hbac_allow=not options.no_hbac_allow, setup_pkinit=not options.no_pkinit) else: api.Backend.ldap2.connect() ds = dsinstance.DsInstance(fstore=fstore, domainlevel=options.domainlevel) installer._ds = ds ds.init_info( realm_name, host_name, domain_name, dm_password, options.subject_base, options.ca_subject, 1101, 1100, None, setup_pkinit=not options.no_pkinit) krb = krbinstance.KrbInstance(fstore) if not options.external_cert_files: krb.create_instance(realm_name, host_name, domain_name, dm_password, master_password, setup_pkinit=not options.no_pkinit, pkcs12_info=pkinit_pkcs12_info, subject_base=options.subject_base) else: krb.init_info(realm_name, host_name, setup_pkinit=not options.no_pkinit, subject_base=options.subject_base) custodia = custodiainstance.get_custodia_instance( options, custodiainstance.CustodiaModes.FIRST_MASTER) custodia.create_instance() if setup_ca: if not options.external_cert_files and options.external_ca: # stage 1 of external CA installation options.realm_name = realm_name options.domain_name = domain_name options.master_password = master_password options.dm_password = dm_password options.admin_password = admin_password options.host_name = host_name options.reverse_zones = dns.reverse_zones cache_vars = {n: options.__dict__[n] for o, n in installer.knobs() if n in options.__dict__} write_cache(cache_vars) ca.install_step_0(False, None, options, custodia=custodia) else: # Put the CA cert where other instances expect it x509.write_certificate(http_ca_cert, paths.IPA_CA_CRT) os.chmod(paths.IPA_CA_CRT, 0o444) if not options.no_pkinit: x509.write_certificate(http_ca_cert, paths.KDC_CA_BUNDLE_PEM) else: with open(paths.KDC_CA_BUNDLE_PEM, 'w'): pass os.chmod(paths.KDC_CA_BUNDLE_PEM, 0o444) x509.write_certificate(http_ca_cert, paths.CA_BUNDLE_PEM) os.chmod(paths.CA_BUNDLE_PEM, 0o444) # we now need to enable ssl on the ds ds.enable_ssl() if setup_ca: ca.install_step_1(False, None, options, custodia=custodia) otpd = otpdinstance.OtpdInstance() otpd.create_instance('OTPD', host_name, ipautil.realm_to_suffix(realm_name)) # Create a HTTP instance http = httpinstance.HTTPInstance(fstore) if options.http_cert_files: http.create_instance( realm_name, host_name, domain_name, dm_password, pkcs12_info=http_pkcs12_info, subject_base=options.subject_base, auto_redirect=not options.no_ui_redirect, ca_is_configured=setup_ca) else: http.create_instance( realm_name, host_name, domain_name, dm_password, subject_base=options.subject_base, auto_redirect=not options.no_ui_redirect, ca_is_configured=setup_ca) ca.set_subject_base_in_config(options.subject_base) # configure PKINIT now that all required services are in place krb.enable_ssl() # Apply any LDAP updates. Needs to be done after the configuration file # is created. DS is restarted in the process. service.print_msg("Applying LDAP updates") ds.apply_updates() # Restart krb after configurations have been changed service.print_msg("Restarting the KDC") krb.restart() if options.setup_kra: kra.install(api, None, options, custodia=custodia) if options.setup_dns: dns.install(False, False, options) if options.setup_adtrust: adtrust.install(False, options, fstore, api) # Set the admin user kerberos password ds.change_admin_password(admin_password) # Call client install script service.print_msg("Configuring client side components") try: args = [paths.IPA_CLIENT_INSTALL, "--on-master", "--unattended", "--domain", domain_name, "--server", host_name, "--realm", realm_name, "--hostname", host_name, "--no-ntp"] if options.no_dns_sshfp: args.append("--no-dns-sshfp") if options.ssh_trust_dns: args.append("--ssh-trust-dns") if options.no_ssh: args.append("--no-ssh") if options.no_sshd: args.append("--no-sshd") if options.mkhomedir: args.append("--mkhomedir") start = time.time() run(args, redirect_output=True) dur = time.time() - start logger.debug("Client install duration: %0.3f", dur, extra={'timing': ('clientinstall', None, None, dur)}) print() except Exception: raise ScriptError("Configuration of client side components failed!") # Enable configured services and update DNS SRV records service.enable_services(host_name) api.Command.dns_update_system_records() if not options.setup_dns: # After DNS and AD trust are configured and services are # enabled, create a dummy instance to dump DNS configuration. bind = bindinstance.BindInstance(fstore) bind.create_file_with_system_records() # Everything installed properly, activate ipa service. services.knownservices.ipa.enable() print("=======================================" "=======================================") print("Setup complete") print("") print("Next steps:") print("\t1. You must make sure these network ports are open:") print("\t\tTCP Ports:") print("\t\t * 80, 443: HTTP/HTTPS") print("\t\t * 389, 636: LDAP/LDAPS") print("\t\t * 88, 464: kerberos") if options.setup_dns: print("\t\t * 53: bind") print("\t\tUDP Ports:") print("\t\t * 88, 464: kerberos") if options.setup_dns: print("\t\t * 53: bind") if not options.no_ntp: print("\t\t * 123: ntp") print("") print("\t2. You can now obtain a kerberos ticket using the command: " "'kinit admin'") print("\t This ticket will allow you to use the IPA tools (e.g., ipa " "user-add)") print("\t and the web user interface.") if not services.knownservices.chronyd.is_running(): print("\t3. Kerberos requires time synchronization between clients") print("\t and servers for correct operation. You should consider " "enabling chronyd.") print("") if setup_ca: print(("Be sure to back up the CA certificates stored in " + paths.CACERT_P12)) print("These files are required to create replicas. The password for " "these") print("files is the Directory Manager password") if os.path.isfile(paths.ROOT_IPA_CACHE): os.remove(paths.ROOT_IPA_CACHE)
def test_delete_cert_and_key(): """Test that delete_cert + delete_key always deletes everything Test with a NSSDB that contains: - cert + key - key only - cert only - none of them """ cmd = ipautil.run(['mktemp'], capture_output=True) p12file = cmd.output.strip() try: with NSSDatabase() as nssdb: nssdb.create_db() # 1. Test delete_key_and_cert when cert + key are present # Create a NSS DB with cert + key create_selfsigned(nssdb) # Save both in a p12 file for latter use ipautil.run([ 'pk12util', '-o', p12file, '-n', CERTNICK, '-d', nssdb.secdir, '-k', nssdb.pwd_file, '-w', nssdb.pwd_file ]) # Delete cert and key nssdb.delete_key_and_cert(CERTNICK) # make sure that everything was deleted assert len(nssdb.list_keys()) == 0 assert len(nssdb.list_certs()) == 0 # 2. Test delete_key_and_cert when only key is present # Import cert and key then remove cert import_args = [ 'pk12util', '-i', p12file, '-d', nssdb.secdir, '-k', nssdb.pwd_file, '-w', nssdb.pwd_file ] ipautil.run(import_args) nssdb.delete_cert(CERTNICK) # Delete cert and key nssdb.delete_key_and_cert(CERTNICK) # make sure that everything was deleted assert len(nssdb.list_keys()) == 0 assert len(nssdb.list_certs()) == 0 # 3. Test delete_key_and_cert when only cert is present # Import cert and key then remove key ipautil.run(import_args) nssdb.delete_key_only(CERTNICK) # make sure the db contains only the cert assert len(nssdb.list_keys()) == 0 assert len(nssdb.list_certs()) == 1 # Delete cert and key when key is not present nssdb.delete_key_and_cert(CERTNICK) # make sure that everything was deleted assert len(nssdb.list_keys()) == 0 assert len(nssdb.list_certs()) == 0 # 4. Test delete_key_and_cert with a wrong nickname # Import cert and key ipautil.run(import_args) # Delete cert and key nssdb.delete_key_and_cert('wrongnick') # make sure that nothing was deleted assert len(nssdb.list_keys()) == 1 assert len(nssdb.list_certs()) == 1 finally: os.unlink(p12file)
def import_files(self, files, import_keys=False, key_password=None, key_nickname=None): """ Import certificates and a single private key from multiple files The files may be in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. :param files: Names of files to import :param import_keys: Whether to import private keys :param key_password: Password to decrypt private keys :param key_nickname: Nickname of the private key to import from PKCS#12 files """ key_file = None extracted_key = None extracted_certs = [] for filename in files: try: with open(filename, 'rb') as f: data = f.read() except IOError as e: raise RuntimeError( "Failed to open %s: %s" % (filename, e.strerror)) # Try to parse the file as PEM file matches = list( re.finditer( br'-----BEGIN (.+?)-----(.*?)-----END \1-----', data, re.DOTALL ) ) if matches: loaded = False for match in matches: body = match.group() label = match.group(1) line = len(data[:match.start() + 1].splitlines()) if label in (b'CERTIFICATE', b'X509 CERTIFICATE', b'X.509 CERTIFICATE'): try: cert = x509.load_pem_x509_certificate(body) except ValueError as e: if label != b'CERTIFICATE': logger.warning( "Skipping certificate in %s at line %s: " "%s", filename, line, e) continue else: extracted_certs.append(cert) loaded = True continue if label in (b'PKCS7', b'PKCS #7 SIGNED DATA', b'CERTIFICATE'): try: certs = x509.pkcs7_to_certs(body) except ipautil.CalledProcessError as e: if label == b'CERTIFICATE': logger.warning( "Skipping certificate in %s at line %s: " "%s", filename, line, e) else: logger.warning( "Skipping PKCS#7 in %s at line %s: %s", filename, line, e) continue else: extracted_certs.extend(certs) loaded = True continue if label in (b'PRIVATE KEY', b'ENCRYPTED PRIVATE KEY', b'RSA PRIVATE KEY', b'DSA PRIVATE KEY', b'EC PRIVATE KEY'): if not import_keys: continue if key_file: raise RuntimeError( "Can't load private key from both %s and %s" % (key_file, filename)) # the args -v2 aes256 -v2prf hmacWithSHA256 are needed # on OpenSSL 1.0.2 (fips mode). As soon as FreeIPA # requires OpenSSL 1.1.0 we'll be able to drop them args = [ paths.OPENSSL, 'pkcs8', '-topk8', '-v2', 'aes256', '-v2prf', 'hmacWithSHA256', '-passout', 'file:' + self.pwd_file, ] if ((label != b'PRIVATE KEY' and key_password) or label == b'ENCRYPTED PRIVATE KEY'): key_pwdfile = ipautil.write_tmp_file(key_password) args += [ '-passin', 'file:' + key_pwdfile.name, ] try: result = ipautil.run( args, stdin=body, capture_output=True) except ipautil.CalledProcessError as e: logger.warning( "Skipping private key in %s at line %s: %s", filename, line, e) continue else: extracted_key = result.raw_output key_file = filename loaded = True continue if loaded: continue raise RuntimeError("Failed to load %s" % filename) # Try to load the file as DER certificate try: cert = x509.load_der_x509_certificate(data) except ValueError: pass else: extracted_certs.append(cert) continue # Try to import the file as PKCS#12 file if import_keys: try: self.import_pkcs12(filename, key_password) except Pkcs12ImportUnknownError: # the file may not be a PKCS#12 file, # go to the generic error about unrecognized format pass except RuntimeError as e: raise RuntimeError("Failed to load %s: %s" % (filename, str(e))) else: if key_file: raise RuntimeError( "Can't load private key from both %s and %s" % (key_file, filename)) key_file = filename server_certs = self.find_server_certs() if key_nickname: for nickname, _trust_flags in server_certs: if nickname == key_nickname: break else: raise RuntimeError( "Server certificate \"%s\" not found in %s" % (key_nickname, filename)) else: if len(server_certs) > 1: raise RuntimeError( "%s server certificates found in %s, " "expecting only one" % (len(server_certs), filename)) continue # Supported formats were tried but none succeeded raise RuntimeError("Failed to load %s: unrecognized format" % filename) if import_keys and not key_file: raise RuntimeError( "No server certificates found in %s" % (', '.join(files))) for cert in extracted_certs: nickname = str(DN(cert.subject)) self.add_cert(cert, nickname, EMPTY_TRUST_FLAGS) if extracted_key: with tempfile.NamedTemporaryFile() as in_file, \ tempfile.NamedTemporaryFile() as out_file: for cert in extracted_certs: in_file.write(cert.public_bytes(x509.Encoding.PEM)) in_file.write(extracted_key) in_file.flush() out_password = ipautil.ipa_generate_password() out_pwdfile = ipautil.write_tmp_file(out_password) args = [ paths.OPENSSL, 'pkcs12', '-export', '-in', in_file.name, '-out', out_file.name, '-passin', 'file:' + self.pwd_file, '-passout', 'file:' + out_pwdfile.name, '-certpbe', 'aes-128-cbc', '-keypbe', 'aes-128-cbc', ] try: ipautil.run(args) except ipautil.CalledProcessError as e: raise RuntimeError( "No matching certificate found for private key from " "%s" % key_file) self.import_pkcs12(out_file.name, out_password)
def create_db(self, user=None, group=None, mode=None, backup=False): """Create cert DB :param user: User owner the secdir :param group: Group owner of the secdir :param mode: Mode of the secdir :param backup: Backup the sedir files """ if mode is not None: dirmode = mode filemode = mode & 0o666 pwdfilemode = mode & 0o660 else: dirmode = 0o750 filemode = 0o640 pwdfilemode = 0o640 uid = -1 gid = -1 if user is not None: uid = pwd.getpwnam(user).pw_uid if group is not None: gid = grp.getgrnam(group).gr_gid if backup: for filename in self.backup_filenames: ipautil.backup_file(filename) if not os.path.exists(self.secdir): os.makedirs(self.secdir, dirmode) if not os.path.exists(self.pwd_file): # Create the password file for this db with io.open(os.open(self.pwd_file, os.O_CREAT | os.O_WRONLY, pwdfilemode), 'w', closefd=True) as f: f.write(ipautil.ipa_generate_password()) # flush and sync tempfile inode f.flush() os.fsync(f.fileno()) # In case dbtype is auto, let certutil decide which type of DB # to create. if self.dbtype == 'auto': dbdir = self.secdir else: dbdir = '{}:{}'.format(self.dbtype, self.secdir) args = [ paths.CERTUTIL, '-d', dbdir, '-N', '-f', self.pwd_file, # -@ in case it's an old db and it must be migrated '-@', self.pwd_file, ] ipautil.run(args, stdin=None, cwd=self.secdir) self._set_filenames(self._detect_dbtype()) if self.filenames is None: # something went wrong... raise ValueError( "Failed to create NSSDB at '{}'".format(self.secdir) ) # Finally fix up perms os.chown(self.secdir, uid, gid) os.chmod(self.secdir, dirmode) tasks.restore_context(self.secdir, force=True) for filename in self.filenames: if os.path.exists(filename): os.chown(filename, uid, gid) if filename == self.pwd_file: new_mode = pwdfilemode else: new_mode = filemode os.chmod(filename, new_mode) tasks.restore_context(filename, force=True)
def run(self): if not is_ipa_configured(): print("IPA is not configured.") return 2 if not cainstance.is_ca_installed_locally(): print("CA is not installed on this server.") return 1 try: ipautil.run(['pki-server', 'cert-fix', '--help'], raiseonerr=True) except ipautil.CalledProcessError: print( "The 'pki-server cert-fix' command is not available; " "cannot proceed." ) return 1 api.bootstrap(in_server=True, confdir=paths.ETC_IPA) api.finalize() api.Backend.ldap2.connect() # ensure DS is up subject_base = dsinstance.DsInstance().find_subject_base() if not subject_base: raise RuntimeError("Cannot determine certificate subject base.") ca_subject_dn = ca.lookup_ca_subject(api, subject_base) now = datetime.datetime.now() + datetime.timedelta(weeks=2) certs, extra_certs = expired_certs(now) if not certs and not extra_certs: print("Nothing to do.") return 0 print(msg) print_intentions(certs, extra_certs) response = ipautil.user_input('Enter "yes" to proceed') if response.lower() != 'yes': print("Not proceeding.") return 0 print("Proceeding.") try: run_cert_fix(certs, extra_certs) except ipautil.CalledProcessError: if any(x[0] is IPACertType.LDAPS for x in extra_certs): # The DS cert was expired. This will cause # 'pki-server cert-fix' to fail at the final # restart. Therefore ignore the CalledProcessError # and proceed to installing the IPA-specific certs. pass else: raise # otherwise re-raise replicate_dogtag_certs(subject_base, ca_subject_dn, certs) install_ipa_certs(subject_base, ca_subject_dn, extra_certs) if any(x[0] != 'sslserver' for x in certs) \ or any(x[0] is IPACertType.IPARA for x in extra_certs): # we renewed a "shared" certificate, therefore we must # become the renewal master print("Becoming renewal master.") cainstance.CAInstance().set_renewal_master() ipautil.run(['ipactl', 'restart'], raiseonerr=True) return 0
def test_join_host(self, host, keytabname): """ Create a test host and join it into IPA. This test must not run remotely. """ if not os.path.isfile(paths.SBIN_IPA_JOIN): pytest.skip("Command '%s' not found. " "The test must not run remotely." % paths.SBIN_IPA_JOIN) # create a test host with bulk enrollment password host.track_create() # manipulate host.attrs to correspond with real attributes of host # after creating it with random password del host.attrs['krbprincipalname'] del host.attrs['krbcanonicalname'] host.attrs['has_password'] = True objclass = list( set(host.attrs['objectclass']) - {u'krbprincipal', u'krbprincipalaux'}) host.attrs['objectclass'] = objclass command = host.make_create_command(force=True) result = command(random=True) random_pass = result['result']['randompassword'] host.attrs['randompassword'] = random_pass host.check_create(result) del host.attrs['randompassword'] # joint the host with the bulk password new_args = [ paths.SBIN_IPA_JOIN, "-s", host.api.env.host, "-h", host.fqdn, "-k", keytabname, "-w", random_pass, "-q", ] try: ipautil.run(new_args) except ipautil.CalledProcessError as e: # join operation may fail on 'adding key into keytab', but # the keytab is not necessary for further tests print(e) # fix host.attrs again to correspond with current state host.attrs['has_keytab'] = True host.attrs['has_password'] = False host.attrs['krbprincipalname'] = [ u'host/%s@%s' % (host.fqdn, host.api.env.realm) ] host.attrs['krbcanonicalname'] = [ u'host/%s@%s' % (host.fqdn, host.api.env.realm) ] host.retrieve() # Try to change the password of enrolled host with specified password command = host.make_update_command(updates=dict( userpassword=u'pass_123')) with pytest.raises(errors.ValidationError): command() # Try to change the password of enrolled host with random password command = host.make_update_command(updates=dict(random=True)) with pytest.raises(errors.ValidationError): command()
def set_hostname(self, hostname): ipautil.run([paths.BIN_HOSTNAMECTL, 'set-hostname', hostname])