View | Details | Raw Unified | Return to bug 36748
Collapse All | Expand All

(-)a/management/univention-management-console-module-diagnostic/debian/control (-2 / +1 lines)
 Lines 17-22   Depends: ${misc:Depends}, Link Here 
17
 python-pycurl,
17
 python-pycurl,
18
 python-psutil,
18
 python-psutil,
19
 python-dnspython,
19
 python-dnspython,
20
 python-pyasn1,
20
 python-paramiko
21
 python-paramiko
21
Description: System Diagnosis UMC module
22
Description: System Diagnosis UMC module
22
 .
23
 .
23
- 
24
--
25
.../umc/python/diagnostic/plugins/kdc_service.py   | 355 +++++++++++++++++++++
24
.../umc/python/diagnostic/plugins/kdc_service.py   | 355 +++++++++++++++++++++
26
1 file changed, 355 insertions(+)
25
1 file changed, 355 insertions(+)
27
create mode 100755 management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/kdc_service.py
26
create mode 100755 management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/kdc_service.py
(-)a/management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/kdc_service.py (-2 / +355 lines)
Line 0    Link Here 
0
- 
1
#!/usr/bin/python2.7
1
--
2
# coding: utf-8
3
#
4
# Univention Management Console module:
5
#  System Diagnosis UMC module
6
#
7
# Copyright 2016-2017 Univention GmbH
8
#
9
# http://www.univention.de/
10
#
11
# All rights reserved.
12
#
13
# The source code of this program is made available
14
# under the terms of the GNU Affero General Public License version 3
15
# (GNU AGPL V3) as published by the Free Software Foundation.
16
#
17
# Binary versions of this program provided by Univention to you as
18
# well as other copyrighted, protected or trademarked materials like
19
# Logos, graphics, fonts, specific documentations and configurations,
20
# cryptographic keys etc. are subject to a license agreement between
21
# you and Univention and not subject to the GNU AGPL V3.
22
#
23
# In the case you use this program under the terms of the GNU AGPL V3,
24
# the program is provided in the hope that it will be useful,
25
# but WITHOUT ANY WARRANTY; without even the implied warranty of
26
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
# GNU Affero General Public License for more details.
28
#
29
# You should have received a copy of the GNU Affero General Public
30
# License with the Debian GNU/Linux or Univention distribution in file
31
# /usr/share/common-licenses/AGPL-3; if not, see
32
# <http://www.gnu.org/licenses/>.
33
34
import struct
35
import socket
36
import random
37
38
import ldap
39
import ipaddr
40
import dns.resolver
41
from pyasn1.type import tag
42
from pyasn1.type import char
43
from pyasn1.type import univ
44
from pyasn1.type import useful
45
from pyasn1.type import namedtype
46
import pyasn1.codec.der.encoder
47
import pyasn1.codec.der.decoder
48
import pyasn1.error
49
50
from univention.config_registry import handler_set as ucr_set
51
import univention.config_registry
52
from univention.management.console.modules.diagnostic import Warning, Critical, ProblemFixed
53
54
from univention.lib.i18n import Translation
55
_ = Translation('univention-management-console-module-diagnostic').translate
56
57
title = _('KDC service check')
58
description = _('The check for the KDC reachability was succesful.')
59
60
61
# This checks for the reachability of KDCs by sending a AS-REQ per TCP and UDP.
62
# The AS-REQ is send with the fake user `kdc-reachability-check`. The KDCs will
63
# respond in several ways: either with an KRB-ERROR (PREAUTH_REQUIRED,
64
# PRINCIPAL_UNKNOWN or RESPONSE_TO_BIG) or a AS-REP with an anonymous ticket.
65
#
66
# If we do not receive one of the above, the connection is not accepted, the
67
# socket is closed or an operation times out, we can assume, that the KDCs is
68
# not reachable.
69
#
70
# This check will test the KDCs as specified in UCR `kerberos/kdc` with TCP and
71
# UDP on port 88. If `kerberos/defaults/dns_lookup_kdc` is set, KDC discovery as
72
# specified in section `7.2.3. KDC Discovery on IP Networks` [1] will be used.
73
# In this case the ports as specified in the SRV records are used.
74
#
75
# This implements a minimal number of packages as defined in [1] and does not
76
# rely on python-kerberos or python-krb5, as those are too high level and
77
# outdated.
78
#
79
# Reachability checks of kpasswd servers are not implemented, as those are a
80
# separate protocol. See [2].
81
#
82
# [1]: https://tools.ietf.org/html/rfc4120
83
# [2]: https://tools.ietf.org/html/rfc3244
84
85
86
def add_lo_to_samba_interfaces():
87
	configRegistry = univention.config_registry.ConfigRegistry()
88
	configRegistry.load()
89
90
	interfaces = configRegistry.get('samba/interfaces', '').split()
91
	interfaces.append('lo')
92
	ucr_set(['samba/interfaces={}'.format(' '.join(interfaces))])
93
	return run(retest=True)
94
95
96
def reset_kerberos_kdc():
97
	ucr_set(['kerberos/kdc=127.0.0.1'])
98
	return run(retest=True)
99
100
101
actions = {
102
	'add_lo_to_samba_interfaces': add_lo_to_samba_interfaces,
103
	'reset_kerberos_kdc': reset_kerberos_kdc,
104
}
105
106
107
def _c(n, t):
108
	return t.clone(tagSet=t.tagSet + tag.Tag(tag.tagClassContext, tag.tagFormatSimple, n))
109
110
111
class PrincipalName(univ.Sequence):
112
	componentType = namedtype.NamedTypes(
113
		namedtype.NamedType('name-type', _c(0, univ.Integer())),
114
		namedtype.NamedType('name-string', _c(1, univ.SequenceOf(componentType=char.GeneralString()))))
115
116
117
class KdcReqBody(univ.Sequence):
118
	tagSet = univ.Sequence.tagSet + tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)
119
	componentType = namedtype.NamedTypes(
120
		namedtype.NamedType('kdc-options', _c(0, univ.BitString())),
121
		namedtype.OptionalNamedType('cname', _c(1, PrincipalName())),
122
		namedtype.NamedType('realm', _c(2, char.GeneralString())),
123
		namedtype.OptionalNamedType('sname', _c(3, PrincipalName())),
124
		namedtype.NamedType('till', _c(5, useful.GeneralizedTime())),
125
		namedtype.NamedType('nonce', _c(7, univ.Integer())),
126
		namedtype.NamedType('etype', _c(8, univ.SequenceOf(componentType=univ.Integer()))))
127
128
129
class PAData(univ.Sequence):
130
	componentType = namedtype.NamedTypes(
131
		namedtype.NamedType('padata-type', _c(1, univ.Integer())),
132
		namedtype.NamedType('padata-value', _c(2, univ.OctetString())))
133
134
135
class AsReq(univ.Sequence):
136
	tagSet = univ.Sequence.tagSet + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 10)
137
	componentType = namedtype.NamedTypes(
138
		namedtype.NamedType('pvno', _c(1, univ.Integer())),
139
		namedtype.NamedType('msg-type', _c(2, univ.Integer())),
140
		namedtype.NamedType('padata', _c(3, univ.SequenceOf(componentType=PAData()))),
141
		namedtype.NamedType('req-body', KdcReqBody()))
142
143
144
class AsRep(univ.Sequence):
145
	tagSet = univ.Sequence.tagSet + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 11)
146
	componentType = namedtype.NamedTypes(
147
		namedtype.NamedType('pvno', _c(0, univ.Integer())),
148
		namedtype.NamedType('msg-type', _c(1, univ.Integer()))
149
		# some more omitted
150
	)
151
152
153
class KrbError(univ.Sequence):
154
	tagSet = univ.Sequence.tagSet + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 30)
155
	componentType = namedtype.NamedTypes(
156
		namedtype.NamedType('pvno', _c(0, univ.Integer())),
157
		namedtype.NamedType('msg-type', _c(1, univ.Integer()))
158
		# some more omitted
159
	)
160
161
162
class KerberosException(Exception):
163
	pass
164
165
166
class ServerUnreachable(KerberosException):
167
	pass
168
169
170
class InvalidResponse(KerberosException):
171
	pass
172
173
174
class EmptyResponse(KerberosException):
175
	pass
176
177
178
def build_kerberos_request(target_realm, user_name):
179
	req_body = KdcReqBody()
180
	req_body['kdc-options'] = "'01010000100000000000000000000000'B"
181
182
	req_body['cname'] = None
183
	req_body['cname']['name-type'] = 1  # NT_PRINCIPAL
184
	req_body['cname']['name-string'] = None
185
	req_body['cname']['name-string'][0] = user_name
186
187
	req_body['realm'] = target_realm
188
189
	req_body['sname'] = None
190
	req_body['sname']['name-type'] = 2  # NT_SRV_INST
191
	req_body['sname']['name-string'] = None
192
	req_body['sname']['name-string'][0] = 'krbtgt'
193
	req_body['sname']['name-string'][1] = target_realm
194
195
	req_body['till'] = '19700101000000Z'
196
	req_body['nonce'] = random.SystemRandom().getrandbits(31)
197
	req_body['etype'] = None
198
	req_body['etype'][0] = 18  # AES256_CTS_HMAC_SHA1_96
199
200
	as_req = AsReq()
201
	as_req['pvno'] = 5
202
	as_req['msg-type'] = 10  # AS-REQ
203
	as_req['padata'] = None
204
	as_req['req-body'] = req_body
205
206
	return pyasn1.codec.der.encoder.encode(as_req)
207
208
209
def send_and_receive(kdc, port, protocol, as_req):
210
	socket_type = socket.SOCK_DGRAM if protocol == 'udp' else socket.SOCK_STREAM
211
	sock = socket.socket(socket.AF_INET, socket_type)
212
	sock.settimeout(1)
213
214
	if protocol == 'tcp':
215
		packed = struct.pack('>I', len(as_req)) + as_req
216
	else:
217
		packed = as_req
218
219
	try:
220
		sock.connect((kdc, port))
221
		sock.sendall(packed)
222
	except socket.error, socket.timeout:
223
		sock.close()
224
		raise ServerUnreachable()
225
226
	received = ''
227
	num_received = 0
228
	if protocol == 'udp':  # fake the length field
229
		received += '\x00\x00\x00\x00'
230
		num_received += 4
231
	while num_received < 128:
232
		try:
233
			(buf, addr) = sock.recvfrom(128)
234
		except socket.error, socket.timeout:
235
			buf = ''
236
		if not buf:
237
			break
238
		received += buf
239
		num_received += len(buf)
240
241
	if not received:
242
		raise EmptyResponse()
243
244
	return received
245
246
247
def probe_kdc(kdc, port, protocol, target_realm, user_name):
248
	request = build_kerberos_request(target_realm, user_name)
249
250
	try:
251
		received = send_and_receive(kdc, port, protocol, request)
252
	except KerberosException:
253
		return False
254
255
	try:
256
		(error, _sub) = pyasn1.codec.der.decoder.decode(received, asn1Spec=KrbError())
257
	except pyasn1.error.PyAsn1Error:
258
		pass
259
	else:
260
		return True
261
262
	try:
263
		(rep, _sub) = pyasn1.codec.der.decoder.decode(received, asn1Spec=AsRep())
264
	except pyasn1.error.PyAsn1Error:
265
		return False
266
267
	return True
268
269
270
def is_service_active(service):
271
	lo = univention.uldap.getMachineConnection()
272
	raw_filter = '(&(univentionService=%s)(cn=%s))'
273
	filter_expr = ldap.filter.filter_format(raw_filter, (service, socket.gethostname()))
274
	for (dn, _attr) in lo.search(filter_expr, attr=['cn']):
275
		if dn is not None:
276
			return True
277
	return False
278
279
280
def run(retest=False):
281
	configRegistry = univention.config_registry.ConfigRegistry()
282
	configRegistry.load()
283
284
	target_realm = configRegistry.get('kerberos/realm')
285
	user_name = 'kdc-reachability-check'
286
287
	kdc_fqds = configRegistry.get('kerberos/kdc', '').split()
288
	dns_lookup_kdc = configRegistry.is_true('kerberos/defaults/dns_lookup_kdc', True)
289
	if not dns_lookup_kdc and not kdc_fqds:
290
		kerberos_dns_fqdn_tcp = '_kerberos._tcp.{}'.format(configRegistry.get('domainname'))
291
		result_tcp = dns.resolver.query(kerberos_dns_fqdn_tcp, 'SRV')
292
		kdc_to_check = [(r.target.to_text(True), r.port, 'tcp') for r in result_tcp]
293
294
		kerberos_dns_fqdn_udp = '_kerberos._udp.{}'.format(configRegistry.get('domainname'))
295
		result_udp = dns.resolver.query(kerberos_dns_fqdn_udp, 'SRV')
296
		kdc_to_check.extend((r.target.to_text(True), r.port, 'udp') for r in result_udp)
297
	else:
298
		kdc_to_check = [(kdc, 88, 'tcp') for kdc in kdc_fqds]
299
		kdc_to_check.extend((kdc, 88, 'udp') for kdc in kdc_fqds)
300
301
	kdc_reachabe = [(probe_kdc(kdc, port, protocol, target_realm, user_name),
302
		(kdc, port, protocol)) for (kdc, port, protocol) in kdc_to_check]
303
	reachable_kdc = [(kdc, port, protocol) for (reachable, (kdc, port, protocol))
304
		in kdc_reachabe if reachable]
305
	unreachable_kdc = [(kdc, port, protocol) for (reachable, (kdc, port, protocol))
306
		in kdc_reachabe if not reachable]
307
308
	error_descriptions = list()
309
310
	if unreachable_kdc:
311
		error = _('The following KDCs were unreachable: {}')
312
		unreach_string = ('{} {}:{}'.format(protocol, kdc, port)
313
			for (kdc, port, protocol) in unreachable_kdc)
314
		error_descriptions.append(error.format(', '.join(unreach_string)))
315
316
	if not reachable_kdc:
317
		is_dc = configRegistry.get('server/role') == 'domaincontroller_master'
318
		is_s4_dc = is_dc and is_service_active('Samba 4')
319
		if is_s4_dc and configRegistry.is_true('samba/interfaces/bindonly', False):
320
			local_included = False
321
			for interface in configRegistry.get('samba/interfaces', '').split():
322
				try:
323
					addr = ipaddr.IPAddress(interface)
324
				except ValueError:
325
					local_included |= interface == 'lo'
326
				else:
327
					local_included |= addr.is_loopback or addr.is_unspecified
328
			error = _('samba/interfaces does not contain lo, 127.0.0.1 or 0.0.0.0.')
329
			error_descriptions.append(error)
330
331
			description = '\n'.join(error_descriptions)
332
			buttons = [{
333
				'action': 'add_lo_to_samba_interfaces',
334
				'label': _('Add lo to samba/interfaces'),
335
			}, {
336
				'action': 'reset_kerberos_kdc',
337
				'label': _('Reset kerberos/kdc to 127.0.0.1'),
338
			}]
339
			raise Critical(description=description, buttons=buttons)
340
341
		error_descriptions.append(_('No reachable KDCs were found.'))
342
		description = '\n'.join(error_descriptions)
343
		raise Critical(description=description)
344
345
	if error_descriptions:
346
		error = '\n'.join(error_descriptions)
347
		raise Warning(description=error)
348
349
	if retest:
350
		raise ProblemFixed()
351
352
353
if __name__ == '__main__':
354
	from univention.management.console.modules.diagnostic import main
355
	main()
2
.../umc/python/diagnostic/de.po                    | 32 ++++++++++++++++++++--
356
.../umc/python/diagnostic/de.po                    | 32 ++++++++++++++++++++--
3
1 file changed, 30 insertions(+), 2 deletions(-)
357
1 file changed, 30 insertions(+), 2 deletions(-)
(-)a/management/univention-management-console-module-diagnostic/umc/python/diagnostic/de.po (-3 / +30 lines)
 Lines 2-9    Link Here 
2
msgid ""
2
msgid ""
3
msgstr ""
3
msgstr ""
4
"Project-Id-Version: univention-management-console-module-diagnostic\n"
4
"Project-Id-Version: univention-management-console-module-diagnostic\n"
5
"Report-Msgid-Bugs-To: packages@univention.de\n"
5
"Report-Msgid-Bugs-To: \n"
6
"POT-Creation-Date: 2016-01-14 12:19+0100\n"
6
"POT-Creation-Date: 2017-05-30 16:19+0200\n"
7
"PO-Revision-Date: \n"
7
"PO-Revision-Date: \n"
8
"Last-Translator: Univention GmbH <packages@univention.de>\n"
8
"Last-Translator: Univention GmbH <packages@univention.de>\n"
9
"Language-Team: Univention GmbH <packages@univention.de>\n"
9
"Language-Team: Univention GmbH <packages@univention.de>\n"
 Lines 23-28   msgstr "" Link Here 
23
"Eine Zeitüberschreitung trat beim Erreichen des Nameservers auf (ist er "
23
"Eine Zeitüberschreitung trat beim Erreichen des Nameservers auf (ist er "
24
"online?)."
24
"online?)."
25
25
26
#: umc/python/diagnostic/plugins/kdc_service.py:334
27
msgid "Add lo to samba/interfaces"
28
msgstr "Füge lo zu samba/interfaces hinzu"
29
26
#: umc/python/diagnostic/plugins/security_limits.py:31
30
#: umc/python/diagnostic/plugins/security_limits.py:31
27
msgid "Adjust to suggested limits"
31
msgid "Adjust to suggested limits"
28
msgstr "An vorgeschlagene Limits anpassen"
32
msgstr "An vorgeschlagene Limits anpassen"
 Lines 59-64   msgstr "" Link Here 
59
"{ucr} oder durch automatische Anpassung auf die empfohlenen Limits zu "
63
"{ucr} oder durch automatische Anpassung auf die empfohlenen Limits zu "
60
"erhöhen."
64
"erhöhen."
61
65
66
#: umc/python/diagnostic/plugins/kdc_service.py:57
67
msgid "KDC service check"
68
msgstr "KDC Erreichbarkeit"
69
62
#: umc/python/diagnostic/plugins/ssh_connection.py:55
70
#: umc/python/diagnostic/plugins/ssh_connection.py:55
63
#, python-format
71
#, python-format
64
msgid ""
72
msgid ""
 Lines 97-102   msgstr "" Link Here 
97
msgid "Nameserver(s) are not responsive"
105
msgid "Nameserver(s) are not responsive"
98
msgstr "Nameserver sind nicht ansprechbar"
106
msgstr "Nameserver sind nicht ansprechbar"
99
107
108
#: umc/python/diagnostic/plugins/kdc_service.py:341
109
msgid "No reachable KDCs were found."
110
msgstr "Keine erreichbaren KDCs gefunden."
111
100
#: umc/python/diagnostic/plugins/package_status.py:11
112
#: umc/python/diagnostic/plugins/package_status.py:11
101
msgid "Package status corrupt"
113
msgid "Package status corrupt"
102
msgstr "Paketstatus korrupt"
114
msgstr "Paketstatus korrupt"
 Lines 129-134   msgstr "" Link Here 
129
msgid "Proxy server failure"
141
msgid "Proxy server failure"
130
msgstr "Proxy-Server-Fehler"
142
msgstr "Proxy-Server-Fehler"
131
143
144
#: umc/python/diagnostic/plugins/kdc_service.py:337
145
msgid "Reset kerberos/kdc to 127.0.0.1"
146
msgstr "Setze kerberos/kdc auf 127.0.0.1 zurück"
147
132
#: umc/python/diagnostic/plugins/ssh_connection.py:16
148
#: umc/python/diagnostic/plugins/ssh_connection.py:16
133
msgid "SSH connection to UCS server failed!"
149
msgid "SSH connection to UCS server failed!"
134
msgstr "SSH-Verbindung zu anderem UCS Server fehlgeschlagen!"
150
msgstr "SSH-Verbindung zu anderem UCS Server fehlgeschlagen!"
 Lines 141-146   msgstr "Sicherheitslimits überschritten" Link Here 
141
msgid "Test again"
157
msgid "Test again"
142
msgstr "Erneut testen"
158
msgstr "Erneut testen"
143
159
160
#: umc/python/diagnostic/plugins/kdc_service.py:58
161
msgid "The check for the KDC reachability was succesful."
162
msgstr "Erreichbarkeitstest der KDCs war erfolgreich."
163
164
#: umc/python/diagnostic/plugins/kdc_service.py:311
165
msgid "The following KDCs were unreachable: {}"
166
msgstr "Die folgenden KDCs waren nicht erreichbar: {}"
167
144
#: umc/python/diagnostic/plugins/ssh_connection.py:48
168
#: umc/python/diagnostic/plugins/ssh_connection.py:48
145
msgid ""
169
msgid ""
146
"The following list shows the affected remote servers and the reason for the "
170
"The following list shows the affected remote servers and the reason for the "
 Lines 260-265   msgstr "" Link Here 
260
"dass Authentifikations-Zugangsdaten (falls existierend) korrekt sind und die "
284
"dass Authentifikations-Zugangsdaten (falls existierend) korrekt sind und die "
261
"ACL's des Proxy-Servers nicht verbieten, Anfragen an %s zu stellen."
285
"ACL's des Proxy-Servers nicht verbieten, Anfragen an %s zu stellen."
262
286
287
#: umc/python/diagnostic/plugins/kdc_service.py:328
288
msgid "samba/interfaces does not contain lo, 127.0.0.1 or 0.0.0.0."
289
msgstr "samba/interfaces enthält weder lo noch 127.0.0.1 noch 0.0.0.0."
290
263
#: umc/python/diagnostic/plugins/package_status.py:28
291
#: umc/python/diagnostic/plugins/package_status.py:28
264
msgid "some"
292
msgid "some"
265
msgstr "einigen"
293
msgstr "einigen"
266
- 

Return to bug 36748