Skip to content

Commit 0a8ca14

Browse files
dpkp88manpreet
authored andcommitted
Small fixes to SASL documentation and logging; validate security_protocol (dpkp#1231)
1 parent 3594901 commit 0a8ca14

File tree

1 file changed

+26
-21
lines changed

1 file changed

+26
-21
lines changed

kafka/conn.py

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ class BrokerConnection(object):
112112
to apply to broker connection sockets. Default:
113113
[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]
114114
security_protocol (str): Protocol used to communicate with brokers.
115-
Valid values are: PLAINTEXT, SSL. Default: PLAINTEXT.
115+
Valid values are: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL.
116+
Default: PLAINTEXT.
116117
ssl_context (ssl.SSLContext): pre-configured SSLContext for wrapping
117118
socket connections. If provided, all other ssl_* configurations
118119
will be ignored. Default: None.
@@ -145,13 +146,15 @@ class BrokerConnection(object):
145146
metrics (kafka.metrics.Metrics): Optionally provide a metrics
146147
instance for capturing network IO stats. Default: None.
147148
metric_group_prefix (str): Prefix for metric names. Default: ''
148-
sasl_mechanism (str): string picking sasl mechanism when security_protocol
149-
is SASL_PLAINTEXT or SASL_SSL. Currently only PLAIN is supported.
150-
Default: None
149+
sasl_mechanism (str): Authentication mechanism when security_protocol
150+
is configured for SASL_PLAINTEXT or SASL_SSL. Valid values are:
151+
PLAIN, GSSAPI. Default: PLAIN
151152
sasl_plain_username (str): username for sasl PLAIN authentication.
152153
Default: None
153154
sasl_plain_password (str): password for sasl PLAIN authentication.
154155
Default: None
156+
sasl_kerberos_service_name (str): Service name to include in GSSAPI
157+
sasl mechanism handshake. Default: 'kafka'
155158
"""
156159

157160
DEFAULT_CONFIG = {
@@ -179,12 +182,10 @@ class BrokerConnection(object):
179182
'sasl_mechanism': 'PLAIN',
180183
'sasl_plain_username': None,
181184
'sasl_plain_password': None,
182-
'sasl_kerberos_service_name':'kafka'
185+
'sasl_kerberos_service_name': 'kafka'
183186
}
184-
if gssapi is None:
185-
SASL_MECHANISMS = ('PLAIN',)
186-
else:
187-
SASL_MECHANISMS = ('PLAIN', 'GSSAPI')
187+
SECURITY_PROTOCOLS = ('PLAINTEXT', 'SSL', 'SASL_PLAINTEXT', 'SASL_SSL')
188+
SASL_MECHANISMS = ('PLAIN', 'GSSAPI')
188189

189190
def __init__(self, host, port, afi, **configs):
190191
self.hostname = host
@@ -210,6 +211,9 @@ def __init__(self, host, port, afi, **configs):
210211
(socket.SOL_SOCKET, socket.SO_SNDBUF,
211212
self.config['send_buffer_bytes']))
212213

214+
assert self.config['security_protocol'] in self.SECURITY_PROTOCOLS, (
215+
'security_protcol must be in ' + ', '.join(self.SECURITY_PROTOCOLS))
216+
213217
if self.config['security_protocol'] in ('SSL', 'SASL_SSL'):
214218
assert ssl_available, "Python wasn't built with SSL support"
215219

@@ -221,7 +225,7 @@ def __init__(self, host, port, afi, **configs):
221225
assert self.config['sasl_plain_password'] is not None, 'sasl_plain_password required for PLAIN sasl'
222226
if self.config['sasl_mechanism'] == 'GSSAPI':
223227
assert gssapi is not None, 'GSSAPI lib not available'
224-
assert self.config['sasl_kerberos_service_name'] is not None, 'sasl_servicename_kafka required for GSSAPI sasl'
228+
assert self.config['sasl_kerberos_service_name'] is not None, 'sasl_kerberos_service_name required for GSSAPI sasl'
225229

226230
self.state = ConnectionStates.DISCONNECTED
227231
self._reset_reconnect_backoff()
@@ -340,6 +344,7 @@ def connect(self):
340344
log.debug('%s: initiating SASL authentication', self)
341345
self.state = ConnectionStates.AUTHENTICATING
342346
else:
347+
# security_protocol PLAINTEXT
343348
log.debug('%s: Connection complete.', self)
344349
self.state = ConnectionStates.CONNECTED
345350
self._reset_reconnect_backoff()
@@ -375,7 +380,6 @@ def connect(self):
375380
if self.state is ConnectionStates.AUTHENTICATING:
376381
assert self.config['security_protocol'] in ('SASL_PLAINTEXT', 'SASL_SSL')
377382
if self._try_authenticate():
378-
log.info('%s: Authenticated as %s', self, self.config['sasl_plain_username'])
379383
log.debug('%s: Connection complete.', self)
380384
self.state = ConnectionStates.CONNECTED
381385
self._reset_reconnect_backoff()
@@ -508,21 +512,21 @@ def _try_authenticate_plain(self, future):
508512
if data != b'\x00\x00\x00\x00':
509513
return future.failure(Errors.AuthenticationFailedError())
510514

515+
log.info('%s: Authenticated as %s', self, self.config['sasl_plain_username'])
511516
return future.success(True)
512517

513518
def _try_authenticate_gssapi(self, future):
514-
515519
data = b''
516520
gssname = self.config['sasl_kerberos_service_name'] + '@' + self.hostname
517-
ctx_Name = gssapi.Name(gssname, name_type=gssapi.NameType.hostbased_service)
521+
ctx_Name = gssapi.Name(gssname, name_type=gssapi.NameType.hostbased_service)
518522
ctx_CanonName = ctx_Name.canonicalize(gssapi.MechType.kerberos)
519523
log.debug('%s: canonical Servicename: %s', self, ctx_CanonName)
520-
ctx_Context = gssapi.SecurityContext(name=ctx_CanonName, usage='initiate')
521-
#Exchange tokens until authentication either suceeded or failed:
524+
ctx_Context = gssapi.SecurityContext(name=ctx_CanonName, usage='initiate')
525+
# Exchange tokens until authentication either succeeds or fails:
522526
received_token = None
523527
try:
524528
while not ctx_Context.complete:
525-
#calculate the output token
529+
# calculate the output token
526530
try:
527531
output_token = ctx_Context.step(received_token)
528532
except GSSError as e:
@@ -541,10 +545,10 @@ def _try_authenticate_gssapi(self, future):
541545
size = Int32.encode(len(msg))
542546
self._sock.sendall(size + msg)
543547

544-
# The server will send a token back. processing of this token either
545-
# establishes a security context, or needs further token exchange
546-
# the gssapi will be able to identify the needed next step
547-
# The connection is closed on failure
548+
# The server will send a token back. Processing of this token either
549+
# establishes a security context, or it needs further token exchange.
550+
# The gssapi will be able to identify the needed next step.
551+
# The connection is closed on failure.
548552
response = self._sock.recv(2000)
549553
self._sock.setblocking(False)
550554

@@ -554,7 +558,7 @@ def _try_authenticate_gssapi(self, future):
554558
future.failure(error)
555559
self.close(error=error)
556560

557-
#pass the received token back to gssapi, strip the first 4 bytes
561+
# pass the received token back to gssapi, strip the first 4 bytes
558562
received_token = response[4:]
559563

560564
except Exception as e:
@@ -563,6 +567,7 @@ def _try_authenticate_gssapi(self, future):
563567
future.failure(error)
564568
self.close(error=error)
565569

570+
log.info('%s: Authenticated as %s', self, gssname)
566571
return future.success(True)
567572

568573
def blacked_out(self):

0 commit comments

Comments
 (0)