mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Implement an installer for the Dogtag certificate system.
The CA is currently not automatically installed. You have to pass in the --ca flag to install it. What works: - installation - unistallation - cert/ra plugins can issue and retrieve server certs What doesn't work: - self-signed CA is still created and issues Apache and DS certs - dogtag and python-nss not in rpm requires - requires that CS be in the "pre" install state from pkicreate
This commit is contained in:
parent
a6294ba041
commit
484eff1016
@ -69,6 +69,8 @@ def parse_options():
|
||||
help="admin user kerberos password")
|
||||
parser.add_option("-d", "--debug", dest="debug", action="store_true",
|
||||
default=False, help="print debugging information")
|
||||
parser.add_option("", "--ca", dest="ca", action="store_true",
|
||||
default=False, help="Configure a CA instance")
|
||||
parser.add_option("--hostname", dest="host_name", help="fully qualified name of server")
|
||||
parser.add_option("--ip-address", dest="ip_address", help="Master Server IP Address")
|
||||
parser.add_option("--setup-bind", dest="setup_bind", action="store_true",
|
||||
@ -298,7 +300,7 @@ def check_dirsrv(unattended):
|
||||
print "\t636"
|
||||
sys.exit(1)
|
||||
|
||||
def uninstall():
|
||||
def uninstall(ca = False):
|
||||
try:
|
||||
run(["/usr/sbin/ipa-client-install", "--on-master", "--unattended", "--uninstall"])
|
||||
except Exception, e:
|
||||
@ -307,6 +309,14 @@ def uninstall():
|
||||
pass
|
||||
|
||||
ntpinstance.NTPInstance(fstore).uninstall()
|
||||
if ca:
|
||||
try:
|
||||
from ipaserver.install import cainstance
|
||||
except ImportError:
|
||||
print >> sys.stderr, "Import failed: %s" % sys.exc_value
|
||||
sys.exit(1)
|
||||
cainstance.CADSInstance().uninstall()
|
||||
cainstance.CAInstance().uninstall()
|
||||
bindinstance.BindInstance(fstore).uninstall()
|
||||
httpinstance.HTTPInstance(fstore).uninstall()
|
||||
krbinstance.KrbInstance(fstore).uninstall()
|
||||
@ -345,7 +355,7 @@ def main():
|
||||
print "Aborting uninstall operation."
|
||||
sys.exit(1)
|
||||
|
||||
return uninstall()
|
||||
return uninstall(options.ca)
|
||||
|
||||
print "=============================================================================="
|
||||
print "This program will setup the FreeIPA Server."
|
||||
@ -495,12 +505,26 @@ def main():
|
||||
os.write(pw_fd, options.dirsrv_pin)
|
||||
os.close(pw_fd)
|
||||
|
||||
if options.ca:
|
||||
try:
|
||||
from ipaserver.install import cainstance
|
||||
except ImportError:
|
||||
print >> sys.stderr, "Import failed: %s" % sys.exc_value
|
||||
sys.exit(1)
|
||||
|
||||
cs = cainstance.CADSInstance()
|
||||
cs.create_instance("dirsrv", realm_name, host_name, domain_name, dm_password)
|
||||
ca = cainstance.CAInstance()
|
||||
ca.configure_instance("pkiuser", host_name, dm_password, dm_password)
|
||||
|
||||
# Create a directory server instance
|
||||
ds = dsinstance.DsInstance()
|
||||
if options.dirsrv_pkcs12:
|
||||
pkcs12_info = (options.dirsrv_pkcs12, pw_name)
|
||||
ds.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, pkcs12_info)
|
||||
os.remove(pw_name)
|
||||
try:
|
||||
ds.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, pkcs12_info)
|
||||
finally:
|
||||
os.remove(pw_name)
|
||||
else:
|
||||
ds.create_instance(ds_user, realm_name, host_name, domain_name, dm_password)
|
||||
|
||||
@ -540,6 +564,8 @@ def main():
|
||||
fd.write("realm=" + realm_name + "\n")
|
||||
fd.write("domain=" + domain_name + "\n")
|
||||
fd.write("xmlrpc_uri=https://%s/ipa/xml\n" % host_name)
|
||||
if options.ca:
|
||||
fd.write("enable_ra=True\n")
|
||||
fd.close()
|
||||
|
||||
bind = bindinstance.BindInstance(fstore)
|
||||
|
@ -37,6 +37,7 @@ import backend
|
||||
import plugable
|
||||
import util
|
||||
from errors2 import PublicError, CommandError, HelpError, InternalError
|
||||
import errors
|
||||
from constants import CLI_TAB
|
||||
from parameters import Password, Bytes
|
||||
from request import ugettext as _
|
||||
|
150
ipapython/nsslib.py
Normal file
150
ipapython/nsslib.py
Normal file
@ -0,0 +1,150 @@
|
||||
# Authors: Rob Crittenden <rcritten@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2009 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2 only
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
import httplib
|
||||
import getpass
|
||||
import socket
|
||||
import errno
|
||||
|
||||
from nss.error import NSPRError
|
||||
import nss.io as io
|
||||
import nss.nss as nss
|
||||
import nss.ssl as ssl
|
||||
|
||||
def client_auth_data_callback(ca_names, chosen_nickname, password, certdb):
|
||||
cert = None
|
||||
if chosen_nickname:
|
||||
try:
|
||||
cert = nss.find_cert_from_nickname(chosen_nickname, password)
|
||||
priv_key = nss.find_key_by_any_cert(cert, password)
|
||||
return cert, priv_key
|
||||
except NSPRError:
|
||||
return False
|
||||
else:
|
||||
nicknames = nss.get_cert_nicknames(certdb, nss.SEC_CERT_NICKNAMES_USER)
|
||||
for nickname in nicknames:
|
||||
try:
|
||||
cert = nss.find_cert_from_nickname(nickname, password)
|
||||
if cert.check_valid_times():
|
||||
if cert.has_signer_in_ca_names(ca_names):
|
||||
priv_key = nss.find_key_by_any_cert(cert, password)
|
||||
return cert, priv_key
|
||||
except NSPRError:
|
||||
return False
|
||||
return False
|
||||
|
||||
class SSLFile(httplib.SSLFile):
|
||||
"""
|
||||
Override the _read method so we can use the NSS recv method.
|
||||
"""
|
||||
def _read(self):
|
||||
buf = ''
|
||||
while True:
|
||||
try:
|
||||
buf = self._ssl.recv(self._bufsize)
|
||||
except NSPRError, e:
|
||||
raise e
|
||||
else:
|
||||
break
|
||||
return buf
|
||||
|
||||
class NSSFakeSocket(httplib.FakeSocket):
|
||||
def makefile(self, mode, bufsize=None):
|
||||
if mode != 'r' and mode != 'rb':
|
||||
raise httplib.UnimplementedFileMode()
|
||||
return SSLFile(self._shared, self._ssl, bufsize)
|
||||
|
||||
def send(self, stuff, flags = 0):
|
||||
return self._ssl.send(stuff)
|
||||
|
||||
sendall = send
|
||||
|
||||
class NSSConnection(httplib.HTTPConnection):
|
||||
default_port = httplib.HTTPSConnection.default_port
|
||||
|
||||
def __init__(self, host, port=None, key_file=None, cert_file=None,
|
||||
ca_file='/etc/pki/tls/certs/ca-bundle.crt', strict=None,
|
||||
dbdir=None):
|
||||
httplib.HTTPConnection.__init__(self, host, port, strict)
|
||||
self.key_file = key_file
|
||||
self.cert_file = cert_file
|
||||
self.ca_file = ca_file
|
||||
|
||||
if not dbdir:
|
||||
raise RuntimeError("dbdir is required")
|
||||
|
||||
ssl.nssinit(dbdir)
|
||||
ssl.set_domestic_policy()
|
||||
nss.set_password_callback(self.password_callback)
|
||||
|
||||
# Create the socket here so we can do things like let the caller
|
||||
# override the NSS callbacks
|
||||
self.sslsock = ssl.SSLSocket()
|
||||
self.sslsock.set_ssl_option(ssl.SSL_SECURITY, True)
|
||||
self.sslsock.set_ssl_option(ssl.SSL_HANDSHAKE_AS_CLIENT, True)
|
||||
self.sslsock.set_handshake_callback(self.handshake_callback)
|
||||
|
||||
def password_callback(self, slot, retry, password):
|
||||
if not retry and password: return password
|
||||
return getpass.getpass("Enter password for %s: " % slot.token_name);
|
||||
|
||||
def handshake_callback(self, sock):
|
||||
"""
|
||||
Verify callback. If we get here then the certificate is ok.
|
||||
"""
|
||||
if self.debuglevel > 0:
|
||||
print "handshake complete, peer = %s" % (sock.get_peer_name())
|
||||
pass
|
||||
|
||||
def connect(self):
|
||||
self.sslsock.set_hostname(self.host)
|
||||
|
||||
net_addr = io.NetworkAddress(self.host, self.port)
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.sslsock.connect(net_addr)
|
||||
self.sock = NSSFakeSocket(sock, self.sslsock)
|
||||
|
||||
class NSSHTTPS(httplib.HTTP):
|
||||
_connection_class = NSSConnection
|
||||
|
||||
def __init__(self, host='', port=None, key_file=None, cert_file=None,
|
||||
ca_file='/etc/pki/tls/certs/ca-bundle.crt', strict=None):
|
||||
# provide a default host, pass the X509 cert info
|
||||
|
||||
# urf. compensate for bad input.
|
||||
if port == 0:
|
||||
port = None
|
||||
self._setup(self._connection_class(host, port, key_file,
|
||||
cert_file, ca_file, strict))
|
||||
# we never actually use these for anything, but we keep them
|
||||
# here for compatibility with post-1.5.2 CVS.
|
||||
self.key_file = key_file
|
||||
self.cert_file = cert_file
|
||||
self.ca_file = ca_file
|
||||
|
||||
if __name__ == "__main__":
|
||||
h = NSSConnection("www.verisign.com", 443, dbdir="/etc/pki/nssdb")
|
||||
h.set_debuglevel(1)
|
||||
h.request("GET", "/")
|
||||
res = h.getresponse()
|
||||
print res.status
|
||||
data = res.read()
|
||||
print data
|
||||
h.close()
|
@ -4,6 +4,7 @@ appdir = $(pythondir)/ipaserver
|
||||
app_PYTHON = \
|
||||
__init__.py \
|
||||
bindinstance.py \
|
||||
cainstance.py \
|
||||
dsinstance.py \
|
||||
ipaldap.py \
|
||||
krbinstance.py \
|
||||
|
743
ipaserver/install/cainstance.py
Normal file
743
ipaserver/install/cainstance.py
Normal file
@ -0,0 +1,743 @@
|
||||
# Authors: Rob Crittenden <rcritten@redhat.com>
|
||||
# Ade Lee <alee@redhat.com>
|
||||
# Andrew Wnuk <awnuk@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2009 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2 only
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
import logging
|
||||
import pwd
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import time
|
||||
import ldap
|
||||
import base64
|
||||
import array
|
||||
import tempfile
|
||||
import binascii
|
||||
import shutil
|
||||
import httplib
|
||||
import urllib
|
||||
import xml.dom.minidom
|
||||
import getopt
|
||||
import urlparse
|
||||
import getpass
|
||||
import socket
|
||||
import errno
|
||||
|
||||
from nss.error import NSPRError
|
||||
import nss.nss as nss
|
||||
|
||||
from ipapython import ipautil
|
||||
from ipapython import nsslib
|
||||
|
||||
from ipaserver.install import service
|
||||
from ipaserver.install import installutils
|
||||
from ipaserver import ipaldap
|
||||
from ipaserver.install import ldapupdate
|
||||
from ipaserver.install import dsinstance
|
||||
from ipalib import util
|
||||
|
||||
DEFAULT_DSPORT=7389
|
||||
|
||||
# We need to reset the template because the CA uses the regular boot
|
||||
# information
|
||||
INF_TEMPLATE = """
|
||||
[General]
|
||||
FullMachineName= $FQHN
|
||||
SuiteSpotUserID= $USER
|
||||
ServerRoot= $SERVER_ROOT
|
||||
[slapd]
|
||||
ServerPort= $DSPORT
|
||||
ServerIdentifier= $SERVERID
|
||||
Suffix= $SUFFIX
|
||||
RootDN= cn=Directory Manager
|
||||
RootDNPwd= $PASSWORD
|
||||
"""
|
||||
|
||||
def get_preop_pin(instance_root, instance_name):
|
||||
preop_pin = None
|
||||
|
||||
filename = instance_root + "/" + instance_name + "/conf/CS.cfg"
|
||||
|
||||
# read the config file and get the preop pin
|
||||
try:
|
||||
f=open(filename)
|
||||
except IOError, e:
|
||||
logging.error("Cannot open configuration file." + str(e))
|
||||
raise e
|
||||
data = f.read()
|
||||
data = data.split('\n')
|
||||
pattern = re.compile("preop.pin=(.*)" )
|
||||
for line in data:
|
||||
match = re.search(pattern, line)
|
||||
if (match):
|
||||
preop_pin=match.group(1)
|
||||
break
|
||||
|
||||
return preop_pin
|
||||
|
||||
def export_pkcs12(output_file, output_passwd, nickname, cert_database,
|
||||
cert_passwd):
|
||||
ipautil.run(["/usr/bin/pk12util", "-d", cert_database,
|
||||
"-o", output_file,
|
||||
"-n", nickname,
|
||||
"-k", cert_passwd,
|
||||
"-w", output_passwd])
|
||||
|
||||
def client_auth_data_callback(ca_names, chosen_nickname, password, certdb):
|
||||
cert = None
|
||||
if chosen_nickname:
|
||||
try:
|
||||
cert = nss.find_cert_from_nickname(chosen_nickname, password)
|
||||
priv_key = nss.find_key_by_any_cert(cert, password)
|
||||
return cert, priv_key
|
||||
except NSPRError, e:
|
||||
logging.debug("client auth callback failed %s" % str(e))
|
||||
return False
|
||||
else:
|
||||
nicknames = nss.get_cert_nicknames(certdb, nss.SEC_CERT_NICKNAMES_USER)
|
||||
for nickname in nicknames:
|
||||
try:
|
||||
cert = nss.find_cert_from_nickname(nickname, password)
|
||||
if cert.check_valid_times():
|
||||
if cert.has_signer_in_ca_names(ca_names):
|
||||
priv_key = nss.find_key_by_any_cert(cert, password)
|
||||
return cert, priv_key
|
||||
except NSPRError, e:
|
||||
logging.debug("client auth callback failed %s" % str(e))
|
||||
return False
|
||||
return False
|
||||
|
||||
def get_value(s):
|
||||
"""
|
||||
Parse out a name/value pair from a Javascript variable.
|
||||
"""
|
||||
try:
|
||||
expr = s.split('=',1)
|
||||
value = expr[1]
|
||||
value = value.replace('\"', '')
|
||||
value = value.replace(';','')
|
||||
value = value.replace('\\n','\n')
|
||||
value = value.replace('\\r','\r')
|
||||
return value
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def find_substring(data, value):
|
||||
"""
|
||||
Scan through a list looking for a string that starts with value.
|
||||
"""
|
||||
for d in data:
|
||||
if d.startswith(value):
|
||||
return get_value(d)
|
||||
|
||||
def get_defList(data):
|
||||
"""
|
||||
Return a dictionary of defList name/value pairs.
|
||||
|
||||
A certificate signing request is specfied as a series of these.
|
||||
"""
|
||||
varname = None
|
||||
value = None
|
||||
skip = False
|
||||
defdict = {}
|
||||
for d in data:
|
||||
if d.startswith("defList = new Object"):
|
||||
varname = None
|
||||
value = None
|
||||
skip = False
|
||||
if d.startswith("defList.defId"):
|
||||
varname = get_value(d)
|
||||
if d.startswith("defList.defVal"):
|
||||
value = get_value(d)
|
||||
if skip:
|
||||
varname = None
|
||||
value = None
|
||||
skip = False
|
||||
if d.startswith("defList.defConstraint"):
|
||||
ctype = get_value(d)
|
||||
if ctype == "readonly":
|
||||
skip = True
|
||||
|
||||
if varname and value:
|
||||
defdict[varname] = value
|
||||
varname = None
|
||||
value = None
|
||||
|
||||
return defdict
|
||||
|
||||
def get_outputList(data):
|
||||
"""
|
||||
Return a dictionary of outputList name/value pairs.
|
||||
|
||||
The output from issuing a certificate is a series of these.
|
||||
"""
|
||||
varname = None
|
||||
value = None
|
||||
outputdict = {}
|
||||
for d in data:
|
||||
if d.startswith("outputList = new"):
|
||||
varname = None
|
||||
value = None
|
||||
if d.startswith("outputList.outputId"):
|
||||
varname = get_value(d)
|
||||
if d.startswith("outputList.outputVal"):
|
||||
value = get_value(d)
|
||||
|
||||
if varname and value:
|
||||
outputdict[varname] = value
|
||||
varname = None
|
||||
value = None
|
||||
|
||||
return outputdict
|
||||
|
||||
class CADSInstance(service.Service):
|
||||
def __init__(self, realm_name=None, domain_name=None, dm_password=None):
|
||||
service.Service.__init__(self, "pkids")
|
||||
self.realm_name = realm_name
|
||||
self.dm_password = dm_password
|
||||
self.sub_dict = None
|
||||
self.domain = domain_name
|
||||
self.serverid = None
|
||||
self.host_name = None
|
||||
self.pkcs12_info = None
|
||||
self.ds_user = None
|
||||
self.ds_port = None
|
||||
if realm_name:
|
||||
self.suffix = util.realm_to_suffix(self.realm_name)
|
||||
self.__setup_sub_dict()
|
||||
else:
|
||||
self.suffix = None
|
||||
|
||||
def create_instance(self, ds_user, realm_name, host_name, domain_name, dm_password, pkcs12_info=None, ds_port=DEFAULT_DSPORT):
|
||||
self.ds_user = ds_user
|
||||
self.ds_port = ds_port
|
||||
self.realm_name = realm_name.upper()
|
||||
self.serverid = "PKI-IPA"
|
||||
self.suffix = util.realm_to_suffix(self.realm_name)
|
||||
self.host_name = host_name
|
||||
self.dm_password = dm_password
|
||||
self.domain = domain_name
|
||||
self.pkcs12_info = pkcs12_info
|
||||
self.__setup_sub_dict()
|
||||
|
||||
self.step("creating directory server user", self.__create_ds_user)
|
||||
self.step("creating directory server instance", self.__create_instance)
|
||||
self.step("configuring directory to start on boot", self.__enable)
|
||||
self.step("restarting directory server", self.__restart_instance)
|
||||
|
||||
self.start_creation("Configuring directory server for the CA:")
|
||||
|
||||
def __setup_sub_dict(self):
|
||||
server_root = dsinstance.find_server_root()
|
||||
self.sub_dict = dict(FQHN=self.host_name, SERVERID=self.serverid,
|
||||
PASSWORD=self.dm_password, SUFFIX=self.suffix.lower(),
|
||||
REALM=self.realm_name, USER=self.ds_user,
|
||||
SERVER_ROOT=server_root, DOMAIN=self.domain,
|
||||
TIME=int(time.time()), DSPORT=self.ds_port)
|
||||
|
||||
def __enable(self):
|
||||
name = self.service_name
|
||||
self.service_name="dirsrv"
|
||||
self.backup_state("enabled", self.is_enabled())
|
||||
self.chkconfig_on()
|
||||
self.service_name = name
|
||||
|
||||
def __create_ds_user(self):
|
||||
user_exists = True
|
||||
try:
|
||||
pwd.getpwnam(self.ds_user)
|
||||
logging.debug("ds user %s exists" % self.ds_user)
|
||||
except KeyError:
|
||||
user_exists = False
|
||||
logging.debug("adding ds user %s" % self.ds_user)
|
||||
args = ["/usr/sbin/useradd", "-c", "DS System User", "-d", "/var/lib/dirsrv", "-M", "-r", "-s", "/sbin/nologin", self.ds_user]
|
||||
try:
|
||||
ipautil.run(args)
|
||||
logging.debug("done adding user")
|
||||
except ipautil.CalledProcessError, e:
|
||||
logging.critical("failed to add user %s" % e)
|
||||
|
||||
self.backup_state("user", self.ds_user)
|
||||
self.backup_state("user_exists", user_exists)
|
||||
|
||||
def __create_instance(self):
|
||||
self.backup_state("running", dsinstance.is_ds_running())
|
||||
self.backup_state("serverid", self.serverid)
|
||||
|
||||
inf_txt = ipautil.template_str(INF_TEMPLATE, self.sub_dict)
|
||||
logging.debug("writing inf template")
|
||||
inf_fd = ipautil.write_tmp_file(inf_txt)
|
||||
inf_txt = re.sub(r"RootDNPwd=.*\n", "", inf_txt)
|
||||
logging.debug(inf_txt)
|
||||
if ipautil.file_exists("/usr/sbin/setup-ds.pl"):
|
||||
args = ["/usr/sbin/setup-ds.pl", "--silent", "--logfile", "-", "-f", inf_fd.name]
|
||||
logging.debug("calling setup-ds.pl")
|
||||
else:
|
||||
args = ["/usr/bin/ds_newinst.pl", inf_fd.name]
|
||||
logging.debug("calling ds_newinst.pl")
|
||||
try:
|
||||
ipautil.run(args)
|
||||
logging.debug("completed creating ds instance")
|
||||
except ipautil.CalledProcessError, e:
|
||||
logging.critical("failed to restart ds instance %s" % e)
|
||||
inf_fd.close()
|
||||
|
||||
def __restart_instance(self):
|
||||
try:
|
||||
# Have to trick the base class to use the right service name
|
||||
sav_name = self.service_name
|
||||
self.service_name="dirsrv"
|
||||
self.restart(self.serverid)
|
||||
self.service_name=sav_name
|
||||
if not dsinstance.is_ds_running():
|
||||
logging.critical("Failed to restart the directory server. See the installation log for details.")
|
||||
sys.exit(1)
|
||||
except Exception, e:
|
||||
# TODO: roll back here?
|
||||
logging.critical("Failed to restart the directory server. See the installation log for details.")
|
||||
|
||||
def uninstall(self):
|
||||
running = self.restore_state("running")
|
||||
enabled = self.restore_state("enabled")
|
||||
serverid = self.restore_state("serverid")
|
||||
sav_name = self.service_name
|
||||
self.service_name="dirsrv"
|
||||
|
||||
if not running is None:
|
||||
self.stop(serverid)
|
||||
|
||||
if not enabled is None and not enabled:
|
||||
self.chkconfig_off()
|
||||
|
||||
if not serverid is None:
|
||||
dsinstance.erase_ds_instance_data(serverid)
|
||||
|
||||
ds_user = self.restore_state("user")
|
||||
user_exists = self.restore_state("user_exists")
|
||||
|
||||
if not ds_user is None and not user_exists is None and not user_exists:
|
||||
try:
|
||||
ipautil.run(["/usr/sbin/userdel", ds_user])
|
||||
except ipautil.CalledProcessError, e:
|
||||
logging.critical("failed to delete user %s" % e)
|
||||
self.service_name = sav_name
|
||||
|
||||
|
||||
class CAInstance(service.Service):
|
||||
def __init__(self):
|
||||
service.Service.__init__(self, "pki-ca")
|
||||
self.pki_user = None
|
||||
self.dm_password = None
|
||||
self.admin_password = None
|
||||
self.host_name = None
|
||||
|
||||
self.basedn = "o=ipaca"
|
||||
self.ca_agent_db = tempfile.mkdtemp(prefix = "tmp-")
|
||||
self.ra_agent_db = "/etc/ipa/ra/alias"
|
||||
self.ra_agent_pwd = self.ra_agent_db + "/.pwd"
|
||||
self.ds_port = DEFAULT_DSPORT
|
||||
self.domain_name = "IPA"
|
||||
self.server_root = "/var/lib"
|
||||
self.secure_port = 9444
|
||||
self.ra_cert = None
|
||||
|
||||
def __del__(self):
|
||||
shutil.rmtree(self.ca_agent_db, ignore_errors=True)
|
||||
|
||||
def configure_instance(self, pki_user, host_name, dm_password, admin_password, ds_port=DEFAULT_DSPORT):
|
||||
self.pki_user = pki_user
|
||||
self.host_name = host_name
|
||||
self.dm_password = dm_password
|
||||
self.admin_password = admin_password
|
||||
self.ds_port = ds_port
|
||||
|
||||
self.step("creating certificate server user", self.__create_ca_user)
|
||||
self.step("configuring certificate server instance", self.__configure_instance)
|
||||
self.step("creating CA agent PKCS#12 file in /root", self.__create_ca_agent_pkcs12)
|
||||
self.step("creating RA agent certificate database", self.__create_ra_agent_db)
|
||||
self.step("importing CA chain to RA certificate database", self.__import_ca_chain)
|
||||
self.step("requesting RA certificate from CA", self.__request_ra_certificate)
|
||||
self.step("issuing RA agent certificate", self.__issue_ra_cert)
|
||||
self.step("adding RA agent as a trusted user", self.__configure_ra)
|
||||
self.step("fixing RA database permissions", self.__fix_ra_perms)
|
||||
self.step("configuring certificate server to start on boot", self.__enable)
|
||||
self.step("restarting certificate server", self.__restart_instance)
|
||||
|
||||
self.start_creation("Configuring certificate server:")
|
||||
|
||||
def __enable(self):
|
||||
self.backup_state("enabled", self.is_enabled())
|
||||
self.chkconfig_on()
|
||||
|
||||
def __create_ca_user(self):
|
||||
user_exists = True
|
||||
try:
|
||||
pwd.getpwnam(self.pki_user)
|
||||
logging.debug("ca user %s exists" % self.pki_user)
|
||||
except KeyError:
|
||||
user_exists = False
|
||||
logging.debug("adding ca user %s" % self.pki_user)
|
||||
args = ["/usr/sbin/useradd", "-c", "CA System User", "-d", "/var/lib", "-M", "-r", "-s", "/sbin/nologin", self.pki_user]
|
||||
try:
|
||||
ipautil.run(args)
|
||||
logging.debug("done adding user")
|
||||
except ipautil.CalledProcessError, e:
|
||||
logging.critical("failed to add user %s" % e)
|
||||
|
||||
self.backup_state("user", self.pki_user)
|
||||
self.backup_state("user_exists", user_exists)
|
||||
|
||||
def __configure_instance(self):
|
||||
#--skipcreate -u pkiuser -g pkiuser -p password -a password -d --hostname `hostname` -n IPA
|
||||
|
||||
preop_pin = get_preop_pin(self.server_root, self.service_name)
|
||||
|
||||
try:
|
||||
args = ["/usr/bin/perl", "/usr/bin/pkisilent", "ConfigureCA",
|
||||
"-cs_hostname", self.host_name,
|
||||
"-cs_port", str(self.secure_port),
|
||||
"-client_certdb_dir", self.ca_agent_db,
|
||||
"-client_certdb_pwd", self.admin_password,
|
||||
"-preop_pin" , preop_pin,
|
||||
"-domain_name", self.domain_name,
|
||||
"-admin_user", "admin",
|
||||
"-admin_email", "root@localhost",
|
||||
"-admin_password", self.admin_password,
|
||||
"-agent_name", "ipa-ca-agent",
|
||||
"-agent_key_size", "2048",
|
||||
"-agent_key_type", "rsa",
|
||||
"-agent_cert_subject", "\"CN=ipa-ca-agent,O=" + self.domain_name + "\"",
|
||||
"-ldap_host", self.host_name,
|
||||
"-ldap_port", str(self.ds_port),
|
||||
"-bind_dn", "\"cn=Directory Manager\"",
|
||||
"-bind_password", self.dm_password,
|
||||
"-base_dn", self.basedn,
|
||||
"-db_name", "ipaca",
|
||||
"-key_size", "2048",
|
||||
"-key_type", "rsa",
|
||||
"-save_p12", "true",
|
||||
"-backup_pwd", self.admin_password,
|
||||
"-subsystem_name", self.service_name,
|
||||
"-token_name", "internal",
|
||||
"-ca_subsystem_cert_subject_name", "\"CN=CA Subsystem Certificate,O=" + self.domain_name + "\"",
|
||||
"-ca_ocsp_cert_subject_name", "\"CN=OCSP Signing Certificate,O=" + self.domain_name + "\"",
|
||||
"-ca_server_cert_subject_name", "CN=" + self.host_name + ",O=" + self.domain_name,
|
||||
"-ca_audit_signing_cert_subject_name", "\"CN=CA Audit Signing Certificate,O=" + self.domain_name + "\"",
|
||||
"-ca_sign_cert_subject_name", "\"CN=Certificate Authority,O=" + self.domain_name + "\"" ]
|
||||
# if (options.external):
|
||||
# pass
|
||||
# args.append("-external")
|
||||
# args.append("true")
|
||||
# args.append("-ext_csr_file")
|
||||
# args.append(ext_csr_file)
|
||||
# if (options.cacertfile):
|
||||
# args.append("-ext_ca_cert_file")
|
||||
# args.append(options.cacertfile)
|
||||
# if (options.cacertchainfile):
|
||||
# args.append("-ext_ca_cert_chain_file")
|
||||
# args.append(options.cacertchainfile)
|
||||
# else:
|
||||
# args.append("-external")
|
||||
# args.append("false")
|
||||
# if (options.clone):
|
||||
# pass
|
||||
# args.append("-clone")
|
||||
# args.append("true")
|
||||
# args.append("-clone_p12_file")
|
||||
# args.append(options.clonefile)
|
||||
# args.append("-clone_p12_password")
|
||||
# args.append(options.clonepasswd)
|
||||
# args.append("-clone_uri")
|
||||
# args.append(options.cloneURI)
|
||||
# args.append("-sd_hostname")
|
||||
# args.append(options.sd_hostname)
|
||||
# args.append("-sd_ssl_port")
|
||||
# args.append(options.sd_ssl_port)
|
||||
# args.append("-sd_admin_name")
|
||||
# args.append(options.sd_admin_name)
|
||||
# args.append("-sd_admin_password")
|
||||
# args.append(options.sd_admin_password)
|
||||
# else:
|
||||
# args.append("-clone")
|
||||
# args.append("false")
|
||||
|
||||
# FIXME
|
||||
args.append("-external")
|
||||
args.append("false")
|
||||
args.append("-clone")
|
||||
args.append("false")
|
||||
|
||||
logging.debug(args)
|
||||
ipautil.run(args)
|
||||
logging.debug("completed creating ca instance")
|
||||
except ipautil.CalledProcessError, e:
|
||||
logging.critical("failed to restart ca instance %s" % e)
|
||||
logging.debug("restarting ca instance")
|
||||
try:
|
||||
self.restart()
|
||||
logging.debug("done restarting ca instance")
|
||||
except ipautil.CalledProcessError, e:
|
||||
print "failed to restart ca instance", e
|
||||
logging.debug("failed to restart ca instance %s" % e)
|
||||
|
||||
def __restart_instance(self):
|
||||
try:
|
||||
self.restart()
|
||||
except Exception, e:
|
||||
# TODO: roll back here?
|
||||
logging.critical("Failed to restart the certificate server. See the installation log for details.")
|
||||
|
||||
def __get_agent_cert(self, nickname):
|
||||
args = ["/usr/bin/certutil", "-L", "-d", self.ca_agent_db, "-n", nickname, "-a"]
|
||||
(out, err) = ipautil.run(args)
|
||||
out = out.replace('-----BEGIN CERTIFICATE-----', '')
|
||||
out = out.replace('-----END CERTIFICATE-----', '')
|
||||
return out
|
||||
|
||||
def __issue_ra_cert(self):
|
||||
# The CA certificate is in the agent DB but isn't trusted
|
||||
(admin_fd, admin_name) = tempfile.mkstemp()
|
||||
os.write(admin_fd, self.admin_password)
|
||||
os.close(admin_fd)
|
||||
|
||||
try:
|
||||
self.__run_certutil(
|
||||
['-M', '-t', 'CT,C,C', '-n',
|
||||
'Certificate Authority - %s' % self.domain_name
|
||||
], database=self.ca_agent_db, pwd_file=self.admin_password)
|
||||
finally:
|
||||
os.remove(admin_name)
|
||||
|
||||
# Retrieve the certificate request so we can get the values needed
|
||||
# to issue a certificate.
|
||||
conn = nsslib.NSSConnection(self.host_name,9443,dbdir=self.ca_agent_db)
|
||||
conn.sslsock.set_client_auth_data_callback(client_auth_data_callback, "ipa-ca-agent", self.admin_password, nss.get_default_certdb())
|
||||
conn.set_debuglevel(0)
|
||||
conn.request("GET", "/ca/agent/ca/profileReview?requestId=7")
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
if res.status != 200:
|
||||
raise RuntimeError("Unable to retrieve certificate request from CA")
|
||||
|
||||
data = data.split('\r\n')
|
||||
params = get_defList(data)
|
||||
params['requestId'] = find_substring(data, "requestId")
|
||||
params['op'] = 'approve'
|
||||
params['submit'] = 'submit'
|
||||
params['requestNotes'] = ''
|
||||
params = urllib.urlencode(params)
|
||||
headers = {"Content-type": "application/x-www-form-urlencoded",
|
||||
"Accept": "text/plain"}
|
||||
|
||||
# Now issue the RA certificate.
|
||||
conn.request("POST", "/ca/agent/ca/profileProcess", params, headers)
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
conn.close()
|
||||
if res.status != 200:
|
||||
raise RuntimeError("Unable to issue RA certificate")
|
||||
|
||||
data = data.split('\r\n')
|
||||
outputList = get_outputList(data)
|
||||
|
||||
self.ra_cert = outputList['b64_cert']
|
||||
self.ra_cert = self.ra_cert.replace('\\n','')
|
||||
self.ra_cert = self.ra_cert.replace('-----BEGIN CERTIFICATE-----','')
|
||||
self.ra_cert = self.ra_cert.replace('-----END CERTIFICATE-----','')
|
||||
|
||||
# Add the new RA cert to the database in /etc/ipa/ra
|
||||
(agent_fd, agent_name) = tempfile.mkstemp()
|
||||
os.write(agent_fd, self.ra_cert)
|
||||
os.close(agent_fd)
|
||||
try:
|
||||
self.__run_certutil(
|
||||
['-A', '-t', 'u,u,u', '-n', 'ipaCert', '-a',
|
||||
'-i', agent_name]
|
||||
)
|
||||
finally:
|
||||
os.remove(agent_name)
|
||||
|
||||
def __configure_ra(self):
|
||||
# Create an RA user in the CA LDAP server and add that user to
|
||||
# the appropriate groups so it can issue certificates without
|
||||
# manual intervention.
|
||||
ld = ldap.initialize("ldap://%s:%d" % (self.host_name, self.ds_port))
|
||||
ld.protocol_version=ldap.VERSION3
|
||||
ld.simple_bind_s("cn=Directory Manager", self.dm_password)
|
||||
|
||||
decoded = base64.b64decode(self.ra_cert)
|
||||
|
||||
entry_dn = "uid=%s,ou=People,%s" % ("ipara", self.basedn)
|
||||
entry = [
|
||||
('objectClass', ['top', 'person', 'organizationalPerson', 'inetOrgPerson', 'cmsuser']),
|
||||
('uid', "ipara"),
|
||||
('sn', "ipara"),
|
||||
('cn', "ipara"),
|
||||
('usertype', "agentType"),
|
||||
('userstate', "1"),
|
||||
('userCertificate;binary', decoded),
|
||||
('description', '2;7;CN=Certificate Authority,O=%s;CN=RA Subsystem Certificate,OU=pki-ipa,O=%s' % (self.domain_name, self.domain_name)),]
|
||||
|
||||
ld.add_s(entry_dn, entry)
|
||||
|
||||
dn = "cn=Certificate Manager Agents,ou=groups,%s" % self.basedn
|
||||
modlist = [(0, 'uniqueMember', '%s' % entry_dn)]
|
||||
ld.modify_s(dn, modlist)
|
||||
|
||||
dn = "cn=Registration Manager Agents,ou=groups,%s" % self.basedn
|
||||
modlist = [(0, 'uniqueMember', '%s' % entry_dn)]
|
||||
ld.modify_s(dn, modlist)
|
||||
|
||||
ld.unbind_s()
|
||||
|
||||
def __run_certutil(self, args, database=None, pwd_file=None,stdin=None):
|
||||
if not database:
|
||||
database = self.ra_agent_db
|
||||
if not pwd_file:
|
||||
pwd_file = self.ra_agent_pwd
|
||||
new_args = ["/usr/bin/certutil", "-d", database, "-f", pwd_file]
|
||||
new_args = new_args + args
|
||||
return ipautil.run(new_args, stdin)
|
||||
|
||||
def __create_ra_agent_db(self):
|
||||
if ipautil.file_exists(self.ra_agent_db + "/cert8.db"):
|
||||
# FIXME, use proper exception
|
||||
raise ValueError("The RA Agent database already exists: %s" % self.ra_agent_db)
|
||||
|
||||
if not ipautil.dir_exists(self.ra_agent_db):
|
||||
os.mkdir(self.ra_agent_db)
|
||||
|
||||
# Create the password file for this db
|
||||
hex_str = binascii.hexlify(os.urandom(10))
|
||||
f = os.open(self.ra_agent_pwd, os.O_CREAT | os.O_RDWR)
|
||||
os.write(f, hex_str)
|
||||
os.close(f)
|
||||
|
||||
stdout, stderr = self.__run_certutil(["-N"])
|
||||
|
||||
def __get_ca_chain(self):
|
||||
conn = httplib.HTTPConnection(self.host_name, 9180)
|
||||
conn.request("GET", "/ca/ee/ca/getCertChain")
|
||||
res = conn.getresponse()
|
||||
if res.status == 200:
|
||||
data = res.read()
|
||||
|
||||
doc = xml.dom.minidom.parseString(data)
|
||||
item_node = doc.getElementsByTagName("ChainBase64")
|
||||
chain = item_node[0].childNodes[0].data
|
||||
doc.unlink()
|
||||
conn.close()
|
||||
|
||||
return chain
|
||||
else:
|
||||
# FIXME: raise proper exception
|
||||
conn.close()
|
||||
raise ValueError("Unable to retrieve CA chain")
|
||||
|
||||
def __create_ca_agent_pkcs12(self):
|
||||
(pwd_fd, pwd_name) = tempfile.mkstemp()
|
||||
os.write(pwd_fd, self.admin_password)
|
||||
os.close(pwd_fd)
|
||||
try:
|
||||
ipautil.run(["/usr/bin/pk12util",
|
||||
"-n", "ipa-ca-agent",
|
||||
"-o", "/root/ca-agent.p12",
|
||||
"-d", self.ca_agent_db,
|
||||
"-k", pwd_name,
|
||||
"-w", pwd_name])
|
||||
finally:
|
||||
os.remove(pwd_name)
|
||||
|
||||
def __import_ca_chain(self):
|
||||
chain = self.__get_ca_chain()
|
||||
(chain_fd, chain_name) = tempfile.mkstemp()
|
||||
os.write(chain_fd, chain)
|
||||
os.close(chain_fd)
|
||||
try:
|
||||
self.__run_certutil(
|
||||
['-A', '-t', 'CT,C,C', '-n', 'caCert', '-a',
|
||||
'-i', chain_name]
|
||||
)
|
||||
finally:
|
||||
os.remove(chain_name)
|
||||
|
||||
def __request_ra_certificate(self):
|
||||
# Create a noise file for generating our private key
|
||||
noise = array.array('B', os.urandom(128))
|
||||
(noise_fd, noise_name) = tempfile.mkstemp()
|
||||
os.write(noise_fd, noise)
|
||||
os.close(noise_fd)
|
||||
|
||||
# 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"])
|
||||
finally:
|
||||
os.remove(noise_name)
|
||||
|
||||
csr = stdout.find("-----BEGIN NEW CERTIFICATE REQUEST-----")
|
||||
if csr >= 0:
|
||||
csr = stdout[csr:]
|
||||
|
||||
# Send the request to the CA
|
||||
conn = httplib.HTTPConnection(self.host_name, 9180)
|
||||
params = urllib.urlencode({'profileId': 'caServerCert',
|
||||
'cert_request_type': 'pkcs10',
|
||||
'requestor_name': 'IPA Installer',
|
||||
'cert_request': csr,
|
||||
'xmlOutput': 'true'})
|
||||
headers = {"Content-type": "application/x-www-form-urlencoded",
|
||||
"Accept": "text/plain"}
|
||||
|
||||
conn.request("POST", "/ca/ee/ca/profileSubmit", params, headers)
|
||||
res = conn.getresponse()
|
||||
if res.status == 200:
|
||||
data = res.read()
|
||||
# print data
|
||||
|
||||
conn.close()
|
||||
else:
|
||||
conn.close()
|
||||
# FIXME: raise proper exception
|
||||
raise ValueError("Unable to submit RA cert request")
|
||||
|
||||
def __fix_ra_perms(self):
|
||||
os.chmod(self.ra_agent_db + "/cert8.db", 0640)
|
||||
os.chmod(self.ra_agent_db + "/key3.db", 0640)
|
||||
os.chmod(self.ra_agent_db + "/secmod.db", 0640)
|
||||
|
||||
pent = pwd.getpwnam("apache")
|
||||
os.chown(self.ra_agent_db + "/cert8.db", 0, pent.pw_gid )
|
||||
os.chown(self.ra_agent_db + "/key3.db", 0, pent.pw_gid )
|
||||
os.chown(self.ra_agent_db + "/secmod.db", 0, pent.pw_gid )
|
||||
os.chown(self.ra_agent_pwd, 0, pent.pw_gid)
|
||||
|
||||
def uninstall(self):
|
||||
try:
|
||||
ipautil.run(["/usr/bin/pkiremove", "-pki_instance_root=/var/lib",
|
||||
"-pki_instance_name=pki-ca", "-force"])
|
||||
except ipautil.CalledProcessError, e:
|
||||
logging.critical("failed to uninstall CA instance %s" % e)
|
||||
|
||||
if __name__ == "__main__":
|
||||
installutils.standard_logging_setup("install.log", False)
|
||||
cs = CADSInstance()
|
||||
cs.create_instance("dirsrv", "GREYOAK.COM", "catest.greyoak.com", "greyoak.com", "password")
|
||||
ca = CAInstance()
|
||||
ca.configure_instance("pkiuser", "catest.greyoak.com", "password", "password")
|
@ -248,7 +248,7 @@ class DsInstance(service.Service):
|
||||
logging.critical("failed to restart ds instance %s" % e)
|
||||
logging.debug("restarting ds instance")
|
||||
try:
|
||||
self.restart()
|
||||
self.restart(self.serverid)
|
||||
logging.debug("done restarting ds instance")
|
||||
except ipautil.CalledProcessError, e:
|
||||
print "failed to restart ds instance", e
|
||||
@ -276,7 +276,7 @@ class DsInstance(service.Service):
|
||||
|
||||
def __restart_instance(self):
|
||||
try:
|
||||
self.restart()
|
||||
self.restart(self.serverid)
|
||||
if not is_ds_running():
|
||||
logging.critical("Failed to restart the directory server. See the installation log for details.")
|
||||
sys.exit(1)
|
||||
|
@ -22,19 +22,19 @@ from ipapython import sysrestore
|
||||
from ipapython import ipautil
|
||||
|
||||
|
||||
def stop(service_name):
|
||||
ipautil.run(["/sbin/service", service_name, "stop"])
|
||||
def stop(service_name, instance_name=""):
|
||||
ipautil.run(["/sbin/service", service_name, "stop", instance_name])
|
||||
|
||||
def start(service_name):
|
||||
ipautil.run(["/sbin/service", service_name, "start"])
|
||||
def start(service_name, instance_name=""):
|
||||
ipautil.run(["/sbin/service", service_name, "start", instance_name])
|
||||
|
||||
def restart(service_name):
|
||||
ipautil.run(["/sbin/service", service_name, "restart"])
|
||||
def restart(service_name, instance_name=""):
|
||||
ipautil.run(["/sbin/service", service_name, "restart", instance_name])
|
||||
|
||||
def is_running(service_name):
|
||||
def is_running(service_name, instance_name=""):
|
||||
ret = True
|
||||
try:
|
||||
ipautil.run(["/sbin/service", service_name, "status"])
|
||||
ipautil.run(["/sbin/service", service_name, "status", instance_name])
|
||||
except ipautil.CalledProcessError:
|
||||
ret = False
|
||||
return ret
|
||||
@ -91,14 +91,14 @@ class Service:
|
||||
def set_output(self, fd):
|
||||
self.output_fd = fd
|
||||
|
||||
def stop(self):
|
||||
stop(self.service_name)
|
||||
def stop(self, instance_name=""):
|
||||
stop(self.service_name, instance_name)
|
||||
|
||||
def start(self):
|
||||
start(self.service_name)
|
||||
def start(self, instance_name=""):
|
||||
start(self.service_name, instance_name)
|
||||
|
||||
def restart(self):
|
||||
restart(self.service_name)
|
||||
def restart(self, instance_name=""):
|
||||
restart(self.service_name, instance_name)
|
||||
|
||||
def is_running(self):
|
||||
return is_running(self.service_name)
|
||||
|
@ -55,7 +55,10 @@ class ra(Backend):
|
||||
Request Authority backend plugin.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.sec_dir = api.env.dot_ipa + os.sep + 'alias'
|
||||
if api.env.home:
|
||||
self.sec_dir = api.env.dot_ipa + os.sep + 'alias'
|
||||
else:
|
||||
self.sec_dir = "/etc/ipa/ra" + os.sep + 'alias'
|
||||
self.pwd_file = self.sec_dir + os.sep + '.pwd'
|
||||
self.noise_file = self.sec_dir + os.sep + '.noise'
|
||||
self.ipa_key_size = "2048"
|
||||
|
Loading…
Reference in New Issue
Block a user