Compare commits

...

7 Commits

Author SHA1 Message Date
Krzysztof Klimonda
c30394fd8e dpkg-query only on bind9 package 2012-04-17 01:07:27 +02:00
Krzysztof Klimonda
bbb86e36aa move check_inst out of bindinstance.py
create a new platform utils module which imports a proper implementation
from platform.PLATFORM.instances.utils. Move the current check_inst to the
fedora16 package and create debian implementation which uses dpkg-query
to check for bind9 and bind-dyndb-ldap packages.
2012-04-17 00:26:03 +02:00
Krzysztof Klimonda
2a40a5f782 rewrite HTTPInstance to be platform agnostic
Moved HTTPInstance class to the base.instances.http module and started
rewriting it to not use hardcoded paths and names, but instead use variables
defined in the implementations provided by the platform modules.
2012-03-09 12:13:18 +01:00
Krzysztof Klimonda
3d3547aae8 Rewrite the base NTPInstance so it's platform-independent
Make NTPInstance use config paths and variables that are set up in the
platform subclass.
2012-03-09 12:13:16 +01:00
Krzysztof Klimonda
ed1d277bf1 use regexp to get current ntpd arguments
Fedora/RHEL uses double quote, but Debian/Ubuntu uses single quote to quote
argument list passed to the daemon. By the replacing current logic with
a simple regexp we remove this difference. It doesn't matter which one is used
so we always save the file with a double quotes.
2012-03-09 12:12:26 +01:00
Krzysztof Klimonda
066849f437 initial import of debian platform modules 2012-03-09 12:12:26 +01:00
Krzysztof Klimonda
2a9868251f convert the base platform module into package
This package will provide base classes for both services and instances
that have to be implemented for each platform to provide needed interface
between FreeIPA and the system it's being run on.
2012-03-09 01:15:32 +01:00
17 changed files with 370 additions and 68 deletions

View File

@@ -0,0 +1 @@
from .ntp import NTPInstance

View File

@@ -36,31 +36,6 @@ from ipalib.constants import DNS_ZONE_REFRESH
import ipalib
from ipalib import api, util, errors
def check_inst(unattended):
has_bind = True
# So far this file is always present in both RHEL5 and Fedora if all the necessary
# bind packages are installed (RHEL5 requires also the pkg: caching-nameserver)
if not os.path.exists('/etc/named.rfc1912.zones'):
print "BIND was not found on this system"
print "Please install the 'bind' package and start the installation again"
has_bind = False
# Also check for the LDAP BIND plug-in
if not os.path.exists('/usr/lib/bind/ldap.so') and \
not os.path.exists('/usr/lib64/bind/ldap.so'):
print "The BIND LDAP plug-in was not found on this system"
print "Please install the 'bind-dyndb-ldap' package and start the installation again"
has_bind = False
if not has_bind:
return False
if not unattended and os.path.exists('/etc/named.conf'):
msg = "Existing BIND configuration detected, overwrite?"
return ipautil.user_input(msg, False)
return True
def normalize_zone(zone):
if zone[-1] != '.':
return zone + '.'

View File

@@ -73,7 +73,7 @@ class HTTPInstance(service.Service):
self.ldap_connect()
self.step("disabling mod_ssl in httpd", self.__disable_mod_ssl)
self.step("disabling mod_ssl in httpd", self._disable_mod_ssl)
self.step("setting mod_nss port to 443", self.__set_mod_nss_port)
self.step("setting mod_nss password file", self.__set_mod_nss_passwordfile)
self.step("enabling mod_nss renegotiate", self.enable_mod_nss_renegotiate)
@@ -129,53 +129,52 @@ class HTTPInstance(service.Service):
def __create_http_keytab(self):
installutils.kadmin_addprinc(self.principal)
installutils.create_keytab("/etc/httpd/conf/ipa.keytab", self.principal)
installutils.create_keytab(self.keytab_path, self.principal)
self.move_service(self.principal)
self.add_cert_to_service()
pent = pwd.getpwnam("apache")
os.chown("/etc/httpd/conf/ipa.keytab", pent.pw_uid, pent.pw_gid)
pent = pwd.getpwnam(self.httpd_user)
os.chown(self.keytab_path, pent.pw_uid, pent.pw_gid)
def __configure_http(self):
target_fname = '/etc/httpd/conf.d/ipa.conf'
target_fname = os.path.join(self.httpd_conf, 'ipa.conf')
http_txt = ipautil.template_file(ipautil.SHARE_DIR + "ipa.conf", self.sub_dict)
self.fstore.backup_file("/etc/httpd/conf.d/ipa.conf")
self.fstore.backup_file(os.path.join(target_fname)
http_fd = open(target_fname, "w")
http_fd.write(http_txt)
http_fd.close()
os.chmod(target_fname, 0644)
target_fname = '/etc/httpd/conf.d/ipa-rewrite.conf'
target_fname = os.path.join(self.httpd_conf, 'ipa-rewrite.conf'
http_txt = ipautil.template_file(ipautil.SHARE_DIR + "ipa-rewrite.conf", self.sub_dict)
self.fstore.backup_file("/etc/httpd/conf.d/ipa-rewrite.conf")
self.fstore.backup_file(target_fname)
http_fd = open(target_fname, "w")
http_fd.write(http_txt)
http_fd.close()
os.chmod(target_fname, 0644)
def __disable_mod_ssl(self):
if os.path.exists(SSL_CONF):
self.fstore.backup_file(SSL_CONF)
os.unlink(SSL_CONF)
def _disable_mod_ssl(self):
# disabling apache2 modules varies from one distribution to another
# so this method has to be implemented by platform modules.
raise NotImplementedError
def __set_mod_nss_port(self):
self.fstore.backup_file(NSS_CONF)
if installutils.update_file(NSS_CONF, '8443', '443') != 0:
self.fstore.backup_file(self.nss_vhost)
if installutils.update_file(self.nss_vhost, '8443', '443') != 0:
print "Updating port in %s failed." % NSS_CONF
def __set_mod_nss_nickname(self, nickname):
installutils.set_directive(NSS_CONF, 'NSSNickname', nickname)
installutils.set_directive(self.nss_vhost, 'NSSNickname', nickname)
def enable_mod_nss_renegotiate(self):
installutils.set_directive(NSS_CONF, 'NSSRenegotiation', 'on', False)
installutils.set_directive(NSS_CONF, 'NSSRequireSafeNegotiation', 'on', False)
installutils.set_directive(self.nss_vhost, 'NSSRenegotiation', 'on', False)
installutils.set_directive(self.nss_vhost, 'NSSRequireSafeNegotiation', 'on', False)
def __set_mod_nss_passwordfile(self):
installutils.set_directive(NSS_CONF, 'NSSPassPhraseDialog', 'file:/etc/httpd/conf/password.conf')
installutils.set_directive(self.nss_vhost, 'NSSPassPhraseDialog', self.password_conf)
def __add_include(self):
"""This should run after __set_mod_nss_port so is already backed up"""
if installutils.update_file(NSS_CONF, '</VirtualHost>', 'Include conf.d/ipa-rewrite.conf\n</VirtualHost>') != 0:
if installutils.update_file(self.nss_vhost, '</VirtualHost>', 'Include conf.d/ipa-rewrite.conf\n</VirtualHost>') != 0:
print "Adding Include conf.d/ipa-rewrite to %s failed." % NSS_CONF
def __setup_ssl(self):
@@ -214,7 +213,7 @@ class HTTPInstance(service.Service):
os.chmod(certs.NSS_DIR + "/secmod.db", 0660)
os.chmod(certs.NSS_DIR + "/pwdfile.txt", 0660)
pent = pwd.getpwnam("apache")
pent = pwd.getpwnam(self.httpd_user)
os.chown(certs.NSS_DIR + "/cert8.db", 0, pent.pw_gid )
os.chown(certs.NSS_DIR + "/key3.db", 0, pent.pw_gid )
os.chown(certs.NSS_DIR + "/secmod.db", 0, pent.pw_gid )
@@ -275,7 +274,10 @@ class HTTPInstance(service.Service):
if not enabled is None and not enabled:
self.disable()
for f in ["/etc/httpd/conf.d/ipa.conf", SSL_CONF, NSS_CONF]:
for f in [os.path.join(self.httpd_conf, "ipa.conf", self.ssl_conf, self.nss_conf, self.nss_vhost]:
if not f:
continue
try:
self.fstore.restore_file(f)
except ValueError, error:
@@ -283,9 +285,9 @@ class HTTPInstance(service.Service):
pass
# Remove the configuration files we create
installutils.remove_file("/etc/httpd/conf.d/ipa-rewrite.conf")
installutils.remove_file("/etc/httpd/conf.d/ipa.conf")
installutils.remove_file("/etc/httpd/conf.d/ipa-pki-proxy.conf")
installutils.remove_file(os.path.join(self.httpd_conf, "ipa-rewrite.conf"))
installutils.remove_file(os.path.join(self.httpd_conf, "ipa.conf"))
installutils.remove_file(os.path.join(self.httpd_conf, "ipa-pki-proxy.conf"))
sebool_state = self.restore_state("httpd_can_network_connect")
if not sebool_state is None:

View File

@@ -19,6 +19,7 @@
#
import logging
import re
import service
from ipapython import sysrestore
@@ -34,21 +35,28 @@ class NTPInstance(service.Service):
else:
self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
def __write_config(self):
def _get_platform_defaults(self):
""" return platform-specific bits for the __write_config()
self.fstore.backup_file("/etc/ntp.conf")
self.fstore.backup_file("/etc/sysconfig/ntpd")
Returns platform-specific bits and should be implemented by
the platform modules. It should return a tuple of two dictionaries:
one with the location of config files, and another with platform-specific
code.
"""
raise NotImplementedError("Should be implemented in the platform code.")
def __write_config(self):
config_files, platform_bits = self._get_platform_defaults()
for path in config_files.values():
self.fstore.backup_file(path)
# We use the OS variable to point it towards either the rhel
# or fedora pools. Other distros should be added in the future
# or we can get our own pool.
os = ""
if ipautil.file_exists("/etc/fedora-release"):
os = "fedora"
elif ipautil.file_exists("/etc/redhat-release"):
os = "rhel"
srv_vals = []
os = platform_bits['os']
srv_vals.append("0.%s.pool.ntp.org" % os)
srv_vals.append("1.%s.pool.ntp.org" % os)
srv_vals.append("2.%s.pool.ntp.org" % os)
@@ -59,7 +67,7 @@ class NTPInstance(service.Service):
file_changed = False
fudge_present = False
ntpconf = []
fd = open("/etc/ntp.conf", "r")
fd = open(config_files['ntp_conf'], "r")
for line in fd:
opt = line.split()
if len(opt) < 1:
@@ -87,7 +95,7 @@ class NTPInstance(service.Service):
ntpconf.append(line)
if file_changed or len(srv_vals) != 0 or not fudge_present:
fd = open("/etc/ntp.conf", "w")
fd = open(config_files['ntp_conf'], "w")
for line in ntpconf:
fd.write(line)
fd.write("\n### Added by IPA Installer ###\n")
@@ -98,19 +106,21 @@ class NTPInstance(service.Service):
fd.write("fudge 127.127.1.0 stratum 10\n")
fd.close()
content_re = '''["'](.*)["']'''
prog = re.compile(content_re)
#read in memory, find OPTIONS, check/change it, then overwrite file
needopts = [ {'val':'-x', 'need':True},
{'val':'-g', 'need':True} ]
fd = open("/etc/sysconfig/ntpd", "r")
fd = open(config_files['ntp_opts'], "r")
lines = fd.readlines()
fd.close()
for line in lines:
sline = line.strip()
if not sline.startswith('OPTIONS'):
if not sline.startswith(platform_bits['opts']):
continue
sline = sline.replace('"', '')
result = prog.match(line).group(1)
for opt in needopts:
if sline.find(opt['val']) != -1:
if result.find(opt['val']) != -1:
opt['need'] = False
newopts = []
@@ -120,16 +130,16 @@ class NTPInstance(service.Service):
done = False
if newopts:
fd = open("/etc/sysconfig/ntpd", "w")
fd = open(config_files['ntp_opts'], "w")
for line in lines:
if not done:
sline = line.strip()
if not sline.startswith('OPTIONS'):
if not sline.startswith(platform_bits['opts']):
fd.write(line)
continue
sline = sline.replace('"', '')
(variable, opts) = sline.split('=', 1)
fd.write('OPTIONS="%s %s"\n' % (opts, ' '.join(newopts)))
fd.write('%s="%s %s"\n' % (platform_bits['opts'], opts, ' '.join(newopts)))
done = True
else:
fd.write(line)
@@ -168,8 +178,9 @@ class NTPInstance(service.Service):
if not running is None:
self.stop()
config_files, platform_bits = self._get_platform_defaults()
try:
self.fstore.restore_file("/etc/ntp.conf")
self.fstore.restore_file(config_files['ntp_conf'])
except ValueError, error:
logging.debug(error)
pass

View File

@@ -0,0 +1,51 @@
import os
from .auth import DebianAuthConfig
from .service import DebianService
from .service import DebianServices
auth = ["authconfig"]
services = ["service", "knownservices"]
utils = ["restore_context", "backup_and_replace_hostname"]
__all__ = auth + services + utils
authconfig = DebianAuthConfig
service = DebianService
knownservices = DebianServices()
def restore_context(filepath):
"""
restore security context on the file path
SELinux equivalent is /sbin/restorecon <filepath>
restorecon's return values are not reliable so we have to
ignore them (BZ #739604). SELinux is optional on Debian systems
so we have to check if /sbin/restorecon exists before calling it.
ipautil.run() will do the logging.
"""
if os.path.exists("/sbin/restorecon"):
ipautil.run(["/sbin/restorecon", filepath], raiseonerr=False)
def backup_and_replace_hostname(fstore, statestore, hostname):
network_filename = "/etc/hostname"
# Backup original /etc/hostname
fstore.backup_file(network_filename)
# Write new configuration
f = open(network_filename, 'w')
f.write(hostname + "\n")
f.close()
try:
ipautil.run(['/bin/hostname', hostname])
except ipautil.CalledProcessError, e:
print >>sys.stderr, "Failed to set this machine hostname to %s (%s)." % (hostname, str(e))
# For SE Linux environments it is important to reset SE labels to the expected ones
try:
restore_context(network_filename)
except ipautil.CalledProcessError, e:
print >>sys.stderr, "Failed to set permissions for %s (%s)." % (network_filename, str(e))

View File

@@ -0,0 +1,21 @@
from ..base import AuthConfig
class DebianAuthConfig(AuthConfig):
"""
Debian implementation of the AuthConfig class.
Debian doesn't provide a single application for changing both
nss and pam configuration. PAM can be configured using debconf but there
is currently no such solution for updating NSS database and every package
does it by itself.
We'll have to play a catch-up game with the rest of the FreeIPA project
filtering out .enable() and .disable() calls that are useless for us,
and making the best out of the rest of them.
"""
# a list of pam config items we can use with pam-auth-update
_pam_configs = ["ldap", "krb5", "mkhomedir"]
def execute(self):
raise NotImplementedError

View File

@@ -0,0 +1 @@
from .ntp import DebianNTPInstance

View File

@@ -0,0 +1,39 @@
# Authors: Krzysztof Klimonda <kklimonda@ubuntu.com>
#
# Copyright (C) 2012 Krzysztof Klimonda
# 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, either version 3 of the License, or
# (at your option) any later version.
#
# 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, see <http://www.gnu.org/licenses/>.
#
import os
from ipapython import ipautil
from ..base.instances import HTTPInstance
class DebianHTTPInstance(HTTPInstance):
def __init__(self):
self.httpd_dir = "/etc/apache2"
self.httpd_conf_dir = os.path.join(self.httpd_dir, "conf.d")
self.nss_conf = os.path.join(self.httpd_dir, "mods-available/nss.conf")
self.nss_vhost = os.path.join(self.httpd_dir, "sites-available/nss")
self.ssl_conf = None # not used by Debian for disabling mod_ssl
self.keytab_path = os.path.join(self.http_dir, "ipa.keytab")
self.httpd_user = "www-data"
self.password_conf = 'file:/etc/apache2/password.conf'
def _disable_mod_ssl(self):
ipautil.run(["a2dismod", "ssl"])
ipautil.run(["a2dissite", "default-ssl"])

View File

@@ -0,0 +1,31 @@
# Authors: Krzysztof Klimonda <kklimonda@ubuntu.com>
#
# Copyright (C) 2012 Krzysztof Klimonda
# 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, either version 3 of the License, or
# (at your option) any later version.
#
# 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, see <http://www.gnu.org/licenses/>.
#
from ...base.instances import NTPInstance
class DebianNTPInstance(NTPInstance):
def __init__(self, fsstore=None):
super(NTPInstance, self).__init__(fsstore)
def _get_platform_bits(self):
return ({'ntp_conf': '/etc/ntp.conf',
'ntp_opts': '/etc/default/ntpd'},
{'opts': 'NTPD_OPTS',
'os': 'ubuntu'})

View File

@@ -0,0 +1,45 @@
import os
import subprocess
import ipautil
from hashutil import md5
def bind_check_installation(unattended):
# files in /etc/ are not being deleted on package removal, so we can't
# rely on them to check if BIND is installed. Also, because Debian has
# switched to the multiarch filesystem layout there is no easy way
# to look for the ldap.so BIND plugin. But we can query dpkg database
# to see if both packages are installed, it actually feels like a cleaner
# solution.
dpkg_query = ['/usr/bin/dpkg-query', '-W', '-f', '${Package}']
try:
ipautil.run(dpkg_query + ['bind'], raiseonerr=True):
except ipautil.CalledProcessError:
print "BIND was not found on this system"
print "Please install the 'bind9' package and start the installation again"
has_bind = False
try:
ipautil.run(dpkg_query + ['bind-dyndb-ldap'], raiseonerr=True)
except ipautil.CalledProcessError:
print "The BIND LDAP plug-in was not found on this system"
print "Please install the 'bind-dyndb-ldap' package and start the installation again"
has_bind = False
if not has_bind:
return False
dpkg_query = ['/usr/bin/dpkg-query', '-W', '-f', '${Conffiles}', 'bind9']
stdout, stderr, ret = ipautil.run(dpkg_query)
# convert dpkg-query output into a mapping fname->md5sum
conf_files = dict([p.strip().split(' ') for p in stdout.split('\n')])
# IPA installation modified named.local so lets check it for local changes
fname = '/etc/bind/named.local'
hash = md5.new(open(fname, 'rb').read()).digest()
if conf_files[fname] != hash:
msg = "Existing BIND configuration detected, overwrite?"
return ipautil.user_input(msg, False)
return True

View File

@@ -0,0 +1,88 @@
from ..base import KnownServices
from ..base import PlatformService
from ..base import wellknownservices
# mappings between rhel-style and debian-style service names
_service_mappings = {
'messagebus': 'dbus',
'ntpd': 'ntp'
}
class DebianService(PlatformService):
def __init__(self, service_name):
try:
service_name = _service_mappings[service_name]
except KeyError:
pass
super(PlatformService, self).__init__()
def _call_service(self, action, instance_name, capture_output):
cmdline = ['/usr/sbin/service', self.service_name, action, instance_name]
return ipautil.run(cmdline, capture_output=capture_output)
def stop(self, instance_name='', capture_output=True):
self._call_service('stop', instance_name, capture_output)
def start(self, instance_name='', capture_output=True):
self._call_service('start', instance_name, capture_output)
def restart(self, instance_name='', capture_output=True):
self._call_service('restart', instance_name, capture_output)
def is_running(self, instance_name=""):
ret = True
try:
(sout, serr, rcode) = self._call_service("status", instance_name)
if sout.find("NOT running") >= 0:
ret = False
if sout.find("stop") >= 0:
ret = False
except ipautil.CalledProcessError:
ret = False
return ret
def is_installed(self):
installed = True
try:
ipautil.run(["/usr/sbin/service", self.service_name, "status"])
except ipautil.CalledProcessError, e:
if e.returncode == 1:
# service is not installed or there is other serious issue
installed = False
return installed
def is_enabled(self):
ret = True
try:
(sout,serr,rcode) = ipautil.run(["/sbin/chkconfig", self.service_name])
if sout.find("off") >= 0:
ret = False
if sout.find("unknown service") >= 0:
ret = False
except ipautil.CalledProcessError:
ret = False
return ret
def enable(self):
ipautil.run(["/sbin/chkconfig", self.service_name, "on"])
def disable(self):
ipautil.run(["/sbin/chkconfig", self.service_name, "off"])
def install(self):
ipautil.run(["/sbin/chkconfig", "--add", self.service_name])
def remove(self):
ipautil.run(["/sbin/chkconfig", "--del", self.service_name])
class DebianServices(KnownServices):
def __init__(self):
services = dict()
for service in wellknownservices:
try:
debian_service = DebianService(_service_mappings[service])
except KeyError:
debian_service = DebianService(service)
services[service] = debian_service
super(DebianServices, self).__init__(services)

View File

View File

@@ -0,0 +1,28 @@
import os
import ipautil
def bind_check_installation(unattended):
has_bind = True
# So far this file is always present in both RHEL5 and Fedora if all the necessary
# bind packages are installed (RHEL5 requires also the pkg: caching-nameserver)
if not os.path.exists('/etc/named.rfc1912.zones'):
print "BIND was not found on this system"
print "Please install the 'bind' package and start the installation again"
has_bind = False
# Also check for the LDAP BIND plug-in
if not os.path.exists('/usr/lib/bind/ldap.so') and \
not os.path.exists('/usr/lib64/bind/ldap.so'):
print "The BIND LDAP plug-in was not found on this system"
print "Please install the 'bind-dyndb-ldap' package and start the installation again"
has_bind = False
if not has_bind:
return False
if not unattended and os.path.exists('/etc/named.conf'):
msg = "Existing BIND configuration detected, overwrite?"
return ipautil.user_input(msg, False)
return True

View File

@@ -0,0 +1,9 @@
"""small utils used by other instances that require platform-dependent
implementations"""
from .SUPPORTED_PLATFORM.utils import *
def bind_check_installation(unattended):
"""checks if the bind server has been properly installed."""
raise NotImplementedError("this function has to be implemented"
"in the platform module")