mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-01-11 00:31:56 -06:00
Make the IPA server host and its services "real" IPA entries
We use kadmin.local to bootstrap the creation of the kerberos principals for the IPA server machine: host, HTTP and ldap. This works fine and has the side-effect of protecting the services from modification by an admin (which would likely break the server). Unfortunately this also means that the services can't be managed by useful utilities such as certmonger. So we have to create them as "real" services instead.
This commit is contained in:
parent
7105a0c0d6
commit
766b534da0
@ -8,7 +8,7 @@ aci: (targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || samba
|
||||
aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword")(version 3.0; acl "Self can write own password"; allow (write) userdn="ldap:///self";)
|
||||
aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Admins can write passwords"; allow (add,delete,write) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";)
|
||||
aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Password change service can read/write passwords"; allow (read, write) userdn="ldap:///krbprincipalname=kadmin/changepw@$REALM,cn=$REALM,cn=kerberos,$SUFFIX";)
|
||||
aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "KDC System Account can access passwords"; allow (all) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
|
||||
aci: (targetattr = "userPassword || krbPrincipalKey || krbPasswordExpiration || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "KDC System Account can access passwords"; allow (all) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
|
||||
aci: (targetattr = "krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount")(version 3.0; acl "KDC System Account can update some fields"; allow (write) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
|
||||
aci: (targetattr = "krbPrincipalName || krbUPEnabled || krbMKey || krbTicketPolicyReference || krbPrincipalExpiration || krbPasswordExpiration || krbPwdPolicyReference || krbPrincipalType || krbPwdHistory || krbLastPwdChange || krbPrincipalAliases || krbExtraData || krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount")(version 3.0; acl "Only the KDC System Account has access to kerberos material"; allow (read, search, compare) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
|
||||
aci: (targetfilter = "(|(objectClass=person)(objectClass=krbPrincipalAux)(objectClass=posixAccount)(objectClass=groupOfNames)(objectClass=posixGroup))")(targetattr != "aci || userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Account Admins can manage Users and Groups"; allow (add, delete, read, write) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";)
|
||||
|
@ -200,7 +200,7 @@ def install_http(config):
|
||||
config.dir + "/http_pin.txt")
|
||||
|
||||
http = httpinstance.HTTPInstance()
|
||||
http.create_instance(config.realm_name, config.host_name, config.domain_name, False, pkcs12_info, self_signed_ca=True)
|
||||
http.create_instance(config.realm_name, config.host_name, config.domain_name, config.dirman_password, False, pkcs12_info, self_signed_ca=True)
|
||||
|
||||
# Now copy the autoconfiguration files
|
||||
if ipautil.file_exists(config.dir + "/preferences.html"):
|
||||
@ -347,6 +347,10 @@ def main():
|
||||
CA.fix_ra_perms()
|
||||
service.restart("httpd")
|
||||
|
||||
# The DS instance is created before the keytab, add the SSL cert we
|
||||
# generated
|
||||
ds.add_cert_to_service()
|
||||
|
||||
# Create the management framework config file
|
||||
fd = open("/etc/ipa/default.conf", "w")
|
||||
fd.write("[global]\n")
|
||||
|
@ -729,6 +729,10 @@ def main():
|
||||
krb = krbinstance.KrbInstance(fstore)
|
||||
krb.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, master_password)
|
||||
|
||||
# The DS instance is created before the keytab, add the SSL cert we
|
||||
# generated
|
||||
ds.add_cert_to_service()
|
||||
|
||||
# Render webui assets:
|
||||
ipautil.run(["/sbin/restorecon", ASSETS_DIR])
|
||||
render_assets()
|
||||
@ -743,10 +747,10 @@ def main():
|
||||
http = httpinstance.HTTPInstance(fstore)
|
||||
if options.http_pkcs12:
|
||||
pkcs12_info = (options.http_pkcs12, pw_name)
|
||||
http.create_instance(realm_name, host_name, domain_name, autoconfig=False, pkcs12_info=pkcs12_info)
|
||||
http.create_instance(realm_name, host_name, domain_name, dm_password, autoconfig=False, pkcs12_info=pkcs12_info)
|
||||
os.remove(pw_name)
|
||||
else:
|
||||
http.create_instance(realm_name, host_name, domain_name, autoconfig=True, self_signed_ca=not options.ca)
|
||||
http.create_instance(realm_name, host_name, domain_name, dm_password, autoconfig=True, self_signed_ca=not options.ca)
|
||||
ipautil.run(["/sbin/restorecon", "/var/cache/ipa/sessions"])
|
||||
|
||||
# Create the management framework config file
|
||||
|
@ -201,6 +201,7 @@ class BindInstance(service.Service):
|
||||
# Store the keytab on disk
|
||||
self.fstore.backup_file("/etc/named.keytab")
|
||||
installutils.create_keytab("/etc/named.keytab", dns_principal)
|
||||
dns_principal = self.move_service(dns_principal)
|
||||
|
||||
# Make sure access is strictly reserved to the named user
|
||||
pent = pwd.getpwnam(self.named_user)
|
||||
@ -220,17 +221,8 @@ class BindInstance(service.Service):
|
||||
logging.critical("Could not connect to the Directory Server on %s" % self.fqdn)
|
||||
raise e
|
||||
|
||||
dns_princ_dn = "krbprincipalname=%s,cn=%s,cn=kerberos,%s" % (dns_principal, self.realm, self.suffix)
|
||||
mod = [(ldap.MOD_ADD, 'objectClass', 'ipaService')]
|
||||
|
||||
try:
|
||||
conn.modify_s(dns_princ_dn, mod)
|
||||
except Exception, e:
|
||||
logging.critical("Could not modify principal's %s entry" % dns_principal)
|
||||
raise e
|
||||
|
||||
dns_group = "cn=dnsserver,cn=rolegroups,cn=accounts,%s" % self.suffix
|
||||
mod = [(ldap.MOD_ADD, 'member', dns_princ_dn)]
|
||||
mod = [(ldap.MOD_ADD, 'member', dns_principal)]
|
||||
|
||||
try:
|
||||
conn.modify_s(dns_group, mod)
|
||||
|
@ -810,7 +810,7 @@ class CAInstance(service.Service):
|
||||
os.close(f)
|
||||
os.chmod(self.ra_agent_pwd, stat.S_IRUSR)
|
||||
|
||||
stdout, stderr = self.__run_certutil(["-N"])
|
||||
(stdout, stderr, returncode) = self.__run_certutil(["-N"])
|
||||
|
||||
def __get_ca_chain(self):
|
||||
try:
|
||||
@ -886,7 +886,7 @@ class CAInstance(service.Service):
|
||||
|
||||
# Generate our CSR. The result gets put into stdout
|
||||
try:
|
||||
(stdout, stderr) = self.__run_certutil(["-R", "-k", "rsa", "-g", "2048", "-s", "CN=RA Subsystem Certificate,OU=pki-ipa,O=%s" % self.domain_name, "-z", noise_name, "-a"])
|
||||
(stdout, stderr, returncode) = self.__run_certutil(["-R", "-k", "rsa", "-g", "2048", "-s", "CN=RA Subsystem Certificate,OU=pki-ipa,O=%s" % self.domain_name, "-z", noise_name, "-a"])
|
||||
finally:
|
||||
os.remove(noise_name)
|
||||
|
||||
|
@ -26,6 +26,7 @@ import urllib
|
||||
import xml.dom.minidom
|
||||
import pwd
|
||||
import fcntl
|
||||
import base64
|
||||
|
||||
from ipapython import nsslib
|
||||
from ipapython import sysrestore
|
||||
@ -459,9 +460,20 @@ class CertDB(object):
|
||||
(out, err) = self.request_cert(subject)
|
||||
cdb.issue_server_cert(self.certreq_fname, self.certder_fname)
|
||||
self.add_cert(self.certder_fname, nickname)
|
||||
fd = open(self.certder_fname, "r")
|
||||
dercert = fd.read()
|
||||
fd.close()
|
||||
|
||||
os.unlink(self.certreq_fname)
|
||||
os.unlink(self.certder_fname)
|
||||
|
||||
# On the off-chance the certificate is base64-encoded
|
||||
try:
|
||||
dercert = base64.b64decode(dercert)
|
||||
except:
|
||||
pass
|
||||
return dercert
|
||||
|
||||
def create_signing_cert(self, nickname, hostname, other_certdb=None, subject=None):
|
||||
cdb = other_certdb
|
||||
if not cdb:
|
||||
|
@ -146,6 +146,7 @@ class DsInstance(service.Service):
|
||||
self.host_name = None
|
||||
self.pkcs12_info = None
|
||||
self.ds_user = None
|
||||
self.dercert = None
|
||||
if realm_name:
|
||||
self.suffix = util.realm_to_suffix(self.realm_name)
|
||||
self.__setup_sub_dict()
|
||||
@ -164,6 +165,7 @@ class DsInstance(service.Service):
|
||||
self.self_signed_ca = self_signed_ca
|
||||
self.uidstart = uidstart
|
||||
self.gidstart = gidstart
|
||||
self.principal = "ldap/%s@%s" % (self.host_name, self.realm_name)
|
||||
self.__setup_sub_dict()
|
||||
|
||||
self.step("creating directory server user", self.__create_ds_user)
|
||||
@ -203,7 +205,7 @@ class DsInstance(service.Service):
|
||||
REALM=self.realm_name, USER=self.ds_user,
|
||||
SERVER_ROOT=server_root, DOMAIN=self.domain,
|
||||
TIME=int(time.time()), UIDSTART=self.uidstart,
|
||||
GIDSTART=self.gidstart)
|
||||
GIDSTART=self.gidstart, HOST=self.host_name)
|
||||
|
||||
def __create_ds_user(self):
|
||||
user_exists = True
|
||||
@ -335,19 +337,20 @@ class DsInstance(service.Service):
|
||||
|
||||
# We only handle one server cert
|
||||
nickname = server_certs[0][0]
|
||||
self.dercert = dsdb.get_cert_from_db(nickname)
|
||||
else:
|
||||
nickname = "Server-Cert"
|
||||
cadb = certs.CertDB(httpinstance.NSS_DIR, host_name=self.host_name)
|
||||
if self.self_signed_ca:
|
||||
cadb.create_self_signed()
|
||||
dsdb.create_from_cacert(cadb.cacert_fname, passwd=None)
|
||||
dsdb.create_server_cert("Server-Cert", self.host_name, cadb)
|
||||
self.dercert = dsdb.create_server_cert("Server-Cert", self.host_name, cadb)
|
||||
dsdb.create_pin_file()
|
||||
else:
|
||||
# FIXME, need to set this nickname in the RA plugin
|
||||
cadb.export_ca_cert('ipaCert', False)
|
||||
dsdb.create_from_cacert(cadb.cacert_fname, passwd=None)
|
||||
dsdb.create_server_cert("Server-Cert", self.host_name, cadb)
|
||||
self.dercert = dsdb.create_server_cert("Server-Cert", self.host_name, cadb)
|
||||
dsdb.create_pin_file()
|
||||
|
||||
conn = ipaldap.IPAdmin("127.0.0.1")
|
||||
|
@ -30,6 +30,7 @@ import dsinstance
|
||||
import installutils
|
||||
from ipapython import sysrestore
|
||||
from ipapython import ipautil
|
||||
from ipalib import util
|
||||
|
||||
HTTPD_DIR = "/etc/httpd"
|
||||
SSL_CONF = HTTPD_DIR + "/conf.d/ssl.conf"
|
||||
@ -55,12 +56,16 @@ class HTTPInstance(service.Service):
|
||||
else:
|
||||
self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
|
||||
|
||||
def create_instance(self, realm, fqdn, domain_name, autoconfig=True, pkcs12_info=None, self_signed_ca=False):
|
||||
def create_instance(self, realm, fqdn, domain_name, dm_password=None, autoconfig=True, pkcs12_info=None, self_signed_ca=False):
|
||||
self.fqdn = fqdn
|
||||
self.realm = realm
|
||||
self.domain = domain_name
|
||||
self.dm_password = dm_password
|
||||
self.suffix = util.realm_to_suffix(self.realm)
|
||||
self.pkcs12_info = pkcs12_info
|
||||
self.self_signed_ca = self_signed_ca
|
||||
self.principal = "HTTP/%s@%s" % (self.fqdn, self.realm)
|
||||
self.dercert = None
|
||||
self.sub_dict = { "REALM" : realm, "FQDN": fqdn, "DOMAIN" : self.domain }
|
||||
|
||||
self.step("disabling mod_ssl in httpd", self.__disable_mod_ssl)
|
||||
@ -68,11 +73,11 @@ class HTTPInstance(service.Service):
|
||||
self.step("Setting mod_nss password file", self.__set_mod_nss_passwordfile)
|
||||
self.step("Adding URL rewriting rules", self.__add_include)
|
||||
self.step("configuring httpd", self.__configure_http)
|
||||
self.step("creating a keytab for httpd", self.__create_http_keytab)
|
||||
self.step("Setting up ssl", self.__setup_ssl)
|
||||
if autoconfig:
|
||||
self.step("Setting up browser autoconfig", self.__setup_autoconfig)
|
||||
self.step("publish CA cert", self.__publish_ca_cert)
|
||||
self.step("creating a keytab for httpd", self.__create_http_keytab)
|
||||
self.step("configuring SELinux for httpd", self.__selinux_config)
|
||||
self.step("restarting httpd", self.__start)
|
||||
self.step("configuring httpd to start on boot", self.__enable)
|
||||
@ -117,6 +122,8 @@ class HTTPInstance(service.Service):
|
||||
http_principal = "HTTP/" + self.fqdn + "@" + self.realm
|
||||
installutils.kadmin_addprinc(http_principal)
|
||||
installutils.create_keytab("/etc/httpd/conf/ipa.keytab", http_principal)
|
||||
self.move_service(http_principal)
|
||||
self.add_cert_to_service()
|
||||
|
||||
pent = pwd.getpwnam("apache")
|
||||
os.chown("/etc/httpd/conf/ipa.keytab", pent.pw_uid, pent.pw_gid)
|
||||
@ -170,16 +177,17 @@ class HTTPInstance(service.Service):
|
||||
db.create_password_conf()
|
||||
# We only handle one server cert
|
||||
nickname = server_certs[0][0]
|
||||
self.dercert = db.get_cert_from_db(nickname)
|
||||
|
||||
self.__set_mod_nss_nickname(nickname)
|
||||
else:
|
||||
if self.self_signed_ca:
|
||||
db.create_from_cacert(ca_db.cacert_fname)
|
||||
db.create_password_conf()
|
||||
db.create_server_cert("Server-Cert", self.fqdn, ca_db)
|
||||
self.dercert = db.create_server_cert("Server-Cert", self.fqdn, ca_db)
|
||||
db.create_signing_cert("Signing-Cert", "Object Signing Cert", ca_db)
|
||||
else:
|
||||
db.create_server_cert("Server-Cert", self.fqdn, ca_db)
|
||||
self.dercert = db.create_server_cert("Server-Cert", self.fqdn, ca_db)
|
||||
db.create_signing_cert("Signing-Cert", "Object Signing Cert", ca_db)
|
||||
db.create_password_conf()
|
||||
|
||||
|
@ -32,6 +32,7 @@ from ipapython import sysrestore
|
||||
from ipapython import ipautil
|
||||
from ipalib import util
|
||||
from ipalib import errors
|
||||
from ipalib import uuid
|
||||
|
||||
from ipaserver import ipaldap
|
||||
|
||||
@ -91,6 +92,40 @@ class KrbInstance(service.Service):
|
||||
else:
|
||||
self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
|
||||
|
||||
def move_service_to_host(self, principal):
|
||||
"""
|
||||
Used to move a host/ service principal created by kadmin.local from
|
||||
cn=kerberos to reside under the host entry.
|
||||
"""
|
||||
conn = None
|
||||
|
||||
service_dn = "krbprincipalname=%s,cn=%s,cn=kerberos,%s" % (principal, self.realm, self.suffix)
|
||||
try:
|
||||
conn = ipaldap.IPAdmin("127.0.0.1")
|
||||
conn.simple_bind_s("cn=directory manager", self.admin_password)
|
||||
except Exception, e:
|
||||
logging.critical("Could not connect to the Directory Server on %s" % self.fqdn)
|
||||
raise e
|
||||
service_entry = conn.getEntry(service_dn, ldap.SCOPE_BASE)
|
||||
conn.deleteEntry(service_dn)
|
||||
|
||||
# Create a host entry for this master
|
||||
host_dn = "fqdn=%s,cn=computers,cn=accounts,%s" % (self.fqdn, self.suffix)
|
||||
host_entry = ipaldap.Entry(host_dn)
|
||||
host_entry.setValues('objectclass', ['top', 'ipaobject', 'nshost', 'ipahost', 'pkiuser', 'krbprincipalaux', 'krbprincipal', 'krbticketpolicyaux'])
|
||||
host_entry.setValue('krbextradata', service_entry.getValue('krbextradata'))
|
||||
host_entry.setValue('krblastpwdchange', service_entry.getValue('krblastpwdchange'))
|
||||
host_entry.setValue('krbpasswordexpiration', service_entry.getValue('krbpasswordexpiration'))
|
||||
host_entry.setValue('krbprincipalname', service_entry.getValue('krbprincipalname'))
|
||||
host_entry.setValue('krbticketflags', service_entry.getValue('krbticketflags'))
|
||||
host_entry.setValue('krbprincipalkey', service_entry.getValue('krbprincipalkey'))
|
||||
host_entry.setValue('serverhostname', self.fqdn.split('.',1)[0])
|
||||
host_entry.setValue('cn', self.fqdn)
|
||||
host_entry.setValue('fqdn', self.fqdn)
|
||||
host_entry.setValue('ipauniqueid', str(uuid.uuid1()))
|
||||
conn.addEntry(host_entry)
|
||||
conn.unbind()
|
||||
|
||||
def __common_setup(self, ds_user, realm_name, host_name, domain_name, admin_password):
|
||||
self.ds_user = ds_user
|
||||
self.fqdn = host_name
|
||||
@ -404,6 +439,7 @@ class KrbInstance(service.Service):
|
||||
def __create_ds_keytab(self):
|
||||
ldap_principal = "ldap/" + self.fqdn + "@" + self.realm
|
||||
installutils.kadmin_addprinc(ldap_principal)
|
||||
self.move_service(ldap_principal)
|
||||
|
||||
self.fstore.backup_file("/etc/dirsrv/ds.keytab")
|
||||
installutils.create_keytab("/etc/dirsrv/ds.keytab", ldap_principal)
|
||||
@ -424,6 +460,8 @@ class KrbInstance(service.Service):
|
||||
os.chown("/etc/krb5.keytab", 0, 0)
|
||||
os.chmod("/etc/krb5.keytab", 0600)
|
||||
|
||||
self.move_service_to_host(host_principal)
|
||||
|
||||
def __export_kadmin_changepw_keytab(self):
|
||||
installutils.kadmin_modprinc("kadmin/changepw", "+requires_preauth")
|
||||
|
||||
|
@ -28,7 +28,7 @@ import sys
|
||||
from ipaserver.install import installutils
|
||||
from ipaserver import ipaldap
|
||||
from ipapython import entity, ipautil
|
||||
from ipalib import util
|
||||
from ipalib import util, uuid
|
||||
from ipalib import errors
|
||||
import ldap
|
||||
import logging
|
||||
@ -124,6 +124,7 @@ class LDAPUpdate:
|
||||
|
||||
def _template_str(self, s):
|
||||
try:
|
||||
self.sub_dict["UUID"] = str(uuid.uuid1())
|
||||
return ipautil.template_str(s, self.sub_dict)
|
||||
except KeyError, e:
|
||||
raise BadSyntax("Unknown template keyword %s" % e)
|
||||
|
@ -22,6 +22,10 @@ import os
|
||||
import tempfile
|
||||
from ipapython import sysrestore
|
||||
from ipapython import ipautil
|
||||
from ipalib import uuid, errors
|
||||
import ldap
|
||||
from ipaserver import ipaldap
|
||||
import base64
|
||||
|
||||
|
||||
def stop(service_name, instance_name=""):
|
||||
@ -98,6 +102,7 @@ class Service:
|
||||
path = ipautil.SHARE_DIR + ldif
|
||||
|
||||
if sub_dict is not None:
|
||||
sub_dict['UUID'] = str(uuid.uuid1())
|
||||
txt = ipautil.template_file(path, sub_dict)
|
||||
fd = ipautil.write_tmp_file(txt)
|
||||
path = fd.name
|
||||
@ -120,6 +125,61 @@ class Service:
|
||||
if fd is not None:
|
||||
fd.close()
|
||||
|
||||
def move_service(self, principal):
|
||||
"""
|
||||
Used to move a principal entry created by kadmin.local from
|
||||
cn=kerberos to cn=services
|
||||
"""
|
||||
dn = "krbprincipalname=%s,cn=%s,cn=kerberos,%s" % (principal, self.realm, self.suffix)
|
||||
try:
|
||||
conn = ipaldap.IPAdmin("127.0.0.1")
|
||||
conn.simple_bind_s("cn=directory manager", self.dm_password)
|
||||
except Exception, e:
|
||||
logging.critical("Could not connect to the Directory Server on %s: %s" % (self.fqdn, str(e)))
|
||||
raise e
|
||||
try:
|
||||
entry = conn.getEntry(dn, ldap.SCOPE_BASE)
|
||||
except errors.NotFound:
|
||||
# There is no service in the wrong location, nothing to do.
|
||||
# This can happen when installing a replica
|
||||
conn.unbind()
|
||||
return
|
||||
newdn = "krbprincipalname=%s,cn=services,cn=accounts,%s" % (principal, self.suffix)
|
||||
conn.deleteEntry(dn)
|
||||
entry.dn = newdn
|
||||
classes = entry.getValues("objectclass")
|
||||
classes = classes + ["ipaobject", "ipaservice", "pkiuser"]
|
||||
entry.setValues("objectclass", list(set(classes)))
|
||||
entry.setValue("ipauniqueid", str(uuid.uuid1()))
|
||||
conn.addEntry(entry)
|
||||
conn.unbind()
|
||||
return newdn
|
||||
|
||||
def add_cert_to_service(self):
|
||||
"""
|
||||
Add a certificate to a service
|
||||
|
||||
This should be passed in DER format but we'll be nice and convert
|
||||
a base64-encoded cert if needed.
|
||||
"""
|
||||
try:
|
||||
self.dercert = base64.b64decode(self.dercert)
|
||||
except Exception:
|
||||
pass
|
||||
dn = "krbprincipalname=%s,cn=services,cn=accounts,%s" % (self.principal, self.suffix)
|
||||
try:
|
||||
conn = ipaldap.IPAdmin("127.0.0.1")
|
||||
conn.simple_bind_s("cn=directory manager", self.dm_password)
|
||||
except Exception, e:
|
||||
logging.critical("Could not connect to the Directory Server on %s: %s" % (self.fqdn, str(e)))
|
||||
raise e
|
||||
mod = [(ldap.MOD_ADD, 'userCertificate', self.dercert)]
|
||||
try:
|
||||
conn.modify_s(dn, mod)
|
||||
except Exception, e:
|
||||
logging.critical("Could not add certificate to service %s entry: %s" % (self.principal, str(e)))
|
||||
conn.unbind()
|
||||
|
||||
def set_output(self, fd):
|
||||
self.output_fd = fd
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user