3 # Copyright (C) 2014 Simo Sorce <simo@redhat.com>
5 # see file 'COPYING' for use and warranty information
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
28 IPA_CONFIG_FILE = '/etc/ipa/default.conf'
29 HTTPD_IPA_KEYTAB = '/etc/httpd/conf/ipa.keytab'
30 IPA_COMMAND = '/usr/bin/ipa'
31 IPA_GETKEYTAB = '/usr/sbin/ipa-getkeytab'
34 NO_CREDS_FOR_KEYTAB = """
35 Valid IPA admin credentials are required to get a keytab.
36 Please kinit with a pivileged user like 'admin' and retry.
39 FAILED_TO_GET_KEYTAB = """
40 A pre-existing keytab was not found and it was not possible to
41 successfully retrieve a new keytab for the IPA server. Please
42 manually provide a keytab or resolve the error that cause this
43 failure (see logs) and retry.
47 class Installer(object):
57 def install_args(self, group):
58 group.add_argument('--ipa', choices=['yes', 'no', 'auto'],
60 help='Helper for IPA joined machines')
62 def conf_init(self, opts):
64 # Do a simple check to see if machine is ipa joined
65 if not os.path.exists(IPA_CONFIG_FILE):
66 logger.info('No IPA configuration file. Skipping ipa helper...')
67 if opts['ipa'] == 'yes':
68 raise Exception('No IPA installation found!')
71 # Get config vars from ipa file
73 from ipapython import config as ipaconfig
75 ipaconfig.init_config()
76 self.realm = ipaconfig.config.get_realm()
77 self.domain = ipaconfig.config.get_domain()
78 self.server = ipaconfig.config.get_server()
80 except Exception, e: # pylint: disable=broad-except
81 logger.info('IPA tools installation found: [%s]', str(e))
82 if opts['ipa'] == 'yes':
83 raise Exception('No IPA installation found!')
86 def get_keytab(self, opts):
88 # Check if we have need ipa tools
89 if not os.path.exists(IPA_GETKEYTAB):
90 logger.info('ipa-getkeytab missing. Will skip keytab creation.')
91 if opts['ipa'] == 'yes':
92 raise Exception('No IPA tools found!')
94 # Check if we already have a keytab for HTTP
95 if 'krb_httpd_keytab' in opts:
96 msg = "Searching for keytab in: %s" % opts['krb_httpd_keytab']
97 print >> sys.stdout, msg,
98 if os.path.exists(opts['krb_httpd_keytab']):
99 print >> sys.stdout, "... Found!"
102 print >> sys.stdout, "... Not found!"
104 msg = "Searching for keytab in: %s" % HTTPD_IPA_KEYTAB
105 print >> sys.stdout, msg,
106 if os.path.exists(HTTPD_IPA_KEYTAB):
107 opts['krb_httpd_keytab'] = HTTPD_IPA_KEYTAB
108 print >> sys.stdout, "... Found!"
111 print >> sys.stdout, "... Not found!"
113 us = socket.gethostname()
114 princ = 'HTTP/%s@%s' % (us, self.realm)
116 # Check we have credentials to access server (for keytab)
117 from ipapython import ipaldap
118 from ipalib import errors as ipaerrors
120 for srv in self.server:
121 msg = "Testing access to server: %s" % srv
122 print >> sys.stdout, msg,
125 c = ipaldap.IPAdmin(host=server)
126 c.do_sasl_gssapi_bind()
128 print >> sys.stdout, "... Succeeded!"
130 except ipaerrors.ACIError, e:
131 # usually this error is returned when we have no
132 # good credentials, ask the user to kinit and retry
133 print >> sys.stderr, NO_CREDS_FOR_KEYTAB
134 logger.error('Invalid credentials: [%s]', repr(e))
135 raise Exception('Invalid credentials: [%s]', str(e))
136 except Exception, e: # pylint: disable=broad-except
137 # for other exceptions let's try to fail later
141 subprocess.check_output([IPA_COMMAND, 'service-add', princ],
142 stderr=subprocess.STDOUT)
143 except subprocess.CalledProcessError, e:
144 # hopefully this means the service already exists
145 # otherwise we'll fail later again
146 logger.info('Error trying to create HTTP service:')
147 logger.info('Cmd> %s\n%s', e.cmd, e.output)
150 msg = "Trying to fetch keytab[%s] for %s" % (
151 opts['krb_httpd_keytab'], princ)
152 print >> sys.stdout, msg,
153 subprocess.check_output([IPA_GETKEYTAB,
154 '-s', server, '-p', princ,
155 '-k', opts['krb_httpd_keytab']],
156 stderr=subprocess.STDOUT)
157 except subprocess.CalledProcessError, e:
158 # unfortunately this one is fatal
159 print >> sys.stderr, FAILED_TO_GET_KEYTAB
160 logger.info('Error trying to get HTTP keytab:')
161 logger.info('Cmd> %s\n%s', e.cmd, e.output)
162 raise Exception('Missing keytab: [%s]' % str(e))
164 # Fixup permissions so only the ipsilon user can read these files
165 pw = pwd.getpwnam(HTTPD_USER)
166 os.chown(opts['krb_httpd_keytab'], pw.pw_uid, pw.pw_gid)
168 def configure_server(self, opts):
169 if opts['ipa'] != 'yes' and opts['ipa'] != 'auto':
172 self.logger = logging.getLogger()
176 self.get_keytab(opts)
178 # Forcibly use krb then pam modules
179 if not 'lm_order' in opts:
180 opts['lm_order'] = []
182 if 'krb' not in opts['lm_order']:
183 opts['lm_order'].insert(0, 'krb')
185 if 'pam' not in opts['lm_order']:
186 opts['lm_order'].append('pam')