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