Skip to content

Commit c09f40e

Browse files
authored
Merge pull request #54 from cisco-ie/hidden-insecure
Add insecure channel support
2 parents aad124f + b9e6306 commit c09f40e

File tree

4 files changed

+81
-43
lines changed

4 files changed

+81
-43
lines changed

src/cisco_gnmi/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,4 @@
3030
from .xe import XEClient
3131
from .builder import ClientBuilder
3232

33-
__version__ = "1.0.8"
33+
__version__ = "1.0.9"

src/cisco_gnmi/builder.py

Lines changed: 65 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,25 @@ def set_secure(
158158
-------
159159
self
160160
"""
161+
self.__secure = True
161162
self.__root_certificates = root_certificates
162163
self.__private_key = private_key
163164
self.__certificate_chain = certificate_chain
164165
return self
165166

167+
def _set_insecure(self):
168+
"""Sets the flag to use an insecure channel.
169+
THIS IS AGAINST SPECIFICATION and should not
170+
be used unless necessary and secure transport
171+
is already well understood.
172+
173+
Returns
174+
-------
175+
self
176+
"""
177+
self.__secure = False
178+
return self
179+
166180
def set_secure_from_file(
167181
self, root_certificates=None, private_key=None, certificate_chain=None
168182
):
@@ -276,44 +290,62 @@ def construct(self):
276290
Client or NXClient or XEClient or XRClient
277291
"""
278292
channel = None
279-
channel_ssl_creds = None
280-
channel_metadata_creds = None
281-
channel_creds = None
282-
channel_ssl_creds = grpc.ssl_channel_credentials(
283-
self.__root_certificates, self.__private_key, self.__certificate_chain
284-
)
285-
if self.__username and self.__password:
286-
LOGGER.debug("Using username/password call authentication.")
287-
channel_metadata_creds = grpc.metadata_call_credentials(
288-
CiscoAuthPlugin(self.__username, self.__password)
289-
)
290-
if channel_ssl_creds and channel_metadata_creds:
291-
LOGGER.debug("Using SSL/metadata authentication composite credentials.")
292-
channel_creds = grpc.composite_channel_credentials(
293-
channel_ssl_creds, channel_metadata_creds
293+
if self.__secure:
294+
LOGGER.debug("Using secure channel.")
295+
channel_metadata_creds = None
296+
if self.__username and self.__password:
297+
LOGGER.debug("Using username/password call authentication.")
298+
channel_metadata_creds = grpc.metadata_call_credentials(
299+
CiscoAuthPlugin(self.__username, self.__password)
300+
)
301+
channel_ssl_creds = grpc.ssl_channel_credentials(
302+
self.__root_certificates, self.__private_key, self.__certificate_chain
294303
)
295-
else:
296-
LOGGER.debug("Using SSL credentials, no metadata authentication.")
297-
channel_creds = channel_ssl_creds
298-
if self.__ssl_target_name_override is not False:
299-
if self.__ssl_target_name_override is None:
300-
if not self.__root_certificates:
301-
raise Exception("Deriving override requires root certificate!")
302-
self.__ssl_target_name_override = get_cn_from_cert(
303-
self.__root_certificates
304+
channel_creds = None
305+
if channel_ssl_creds and channel_metadata_creds:
306+
LOGGER.debug("Using SSL/metadata authentication composite credentials.")
307+
channel_creds = grpc.composite_channel_credentials(
308+
channel_ssl_creds, channel_metadata_creds
309+
)
310+
else:
311+
LOGGER.debug(
312+
"Using SSL credentials, no channel metadata authentication."
304313
)
305-
LOGGER.warning(
306-
"Overriding SSL option from certificate could increase MITM susceptibility!"
314+
channel_creds = channel_ssl_creds
315+
if self.__ssl_target_name_override is not False:
316+
if self.__ssl_target_name_override is None:
317+
if not self.__root_certificates:
318+
raise Exception("Deriving override requires root certificate!")
319+
self.__ssl_target_name_override = get_cn_from_cert(
320+
self.__root_certificates
321+
)
322+
LOGGER.warning(
323+
"Overriding SSL option from certificate could increase MITM susceptibility!"
324+
)
325+
self.set_channel_option(
326+
"grpc.ssl_target_name_override", self.__ssl_target_name_override
307327
)
308-
self.set_channel_option(
309-
"grpc.ssl_target_name_override", self.__ssl_target_name_override
328+
channel = grpc.secure_channel(
329+
self.__target_netloc.netloc, channel_creds, self.__channel_options
310330
)
311-
channel = grpc.secure_channel(
312-
self.__target_netloc.netloc, channel_creds, self.__channel_options
313-
)
331+
else:
332+
LOGGER.warning(
333+
"Insecure gRPC channel is against gNMI specification, personal data may be compromised."
334+
)
335+
channel = grpc.insecure_channel(self.__target_netloc.netloc)
314336
if self.__client_class is None:
315337
self.set_os()
316-
client = self.__client_class(channel)
338+
client = None
339+
if self.__secure:
340+
client = self.__client_class(channel)
341+
else:
342+
client = self.__client_class(
343+
channel,
344+
default_call_metadata=[
345+
("username", self.__username),
346+
("password", self.__password),
347+
],
348+
)
317349
self._reset()
318350
return client
319351

@@ -333,4 +365,5 @@ def _reset(self):
333365
self.__password = None
334366
self.__channel_options = None
335367
self.__ssl_target_name_override = False
368+
self.__secure = True
336369
return self

src/cisco_gnmi/cli.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,9 @@ def __gen_client(args):
293293
builder = ClientBuilder(args.netloc)
294294
builder.set_os(args.os)
295295
builder.set_call_authentication(args.username, args.password)
296-
if not any([args.root_certificates, args.private_key, args.certificate_chain]):
296+
if args.insecure:
297+
builder._set_insecure()
298+
elif not any([args.root_certificates, args.private_key, args.certificate_chain]):
297299
builder.set_secure_from_target()
298300
else:
299301
builder.set_secure_from_file(
@@ -339,6 +341,7 @@ def __common_args_handler(parser):
339341
action="store_true",
340342
)
341343
parser.add_argument("-debug", help="Print debug messages.", action="store_true")
344+
parser.add_argument("-insecure", help=argparse.SUPPRESS, action="store_true")
342345
args = parser.parse_args(sys.argv[2:])
343346
logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO)
344347
args.username = input("Username: ")

src/cisco_gnmi/client.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,21 @@ class Client(object):
8383
# gNMI uses nanoseconds, baseline to seconds
8484
_NS_IN_S = int(1e9)
8585

86-
def __init__(self, grpc_channel, timeout=_C_MAX_LONG):
86+
def __init__(self, grpc_channel, timeout=_C_MAX_LONG, default_call_metadata=None):
8787
"""gNMI initialization wrapper which simply wraps some aspects of the gNMI stub.
8888
8989
Parameters
9090
----------
9191
grpc_channel : grpc.Channel
9292
The gRPC channel to initialize the gNMI stub with.
9393
Use ClientBuilder if unfamiliar with gRPC.
94-
username : str
95-
Username to authenticate gNMI RPCs.
96-
password : str
97-
Password to authenticate gNMI RPCs.
9894
timeout : uint
9995
Timeout for gRPC functionality.
96+
default_call_metadata : list
97+
Metadata to be sent with each gRPC call.
10098
"""
10199
self.service = proto.gnmi_pb2_grpc.gNMIStub(grpc_channel)
100+
self.default_call_metadata = default_call_metadata
102101

103102
def capabilities(self):
104103
"""Capabilities allows the client to retrieve the set of capabilities that
@@ -114,7 +113,9 @@ def capabilities(self):
114113
"""
115114
message = proto.gnmi_pb2.CapabilityRequest()
116115
LOGGER.debug(str(message))
117-
response = self.service.Capabilities(message)
116+
response = self.service.Capabilities(
117+
message, metadata=self.default_call_metadata
118+
)
118119
return response
119120

120121
def get(
@@ -171,7 +172,7 @@ def get(
171172

172173
LOGGER.debug(str(request))
173174

174-
get_response = self.service.Get(request)
175+
get_response = self.service.Get(request, metadata=self.default_call_metadata)
175176
return get_response
176177

177178
def set(
@@ -218,7 +219,7 @@ def set(
218219

219220
LOGGER.debug(str(request))
220221

221-
response = self.service.Set(request)
222+
response = self.service.Set(request, metadata=self.default_call_metadata)
222223
return response
223224

224225
def subscribe(self, request_iter, extensions=None):
@@ -261,7 +262,8 @@ def validate_request(request):
261262
return subscribe_request
262263

263264
response_stream = self.service.Subscribe(
264-
(validate_request(request) for request in request_iter)
265+
(validate_request(request) for request in request_iter),
266+
metadata=self.default_call_metadata,
265267
)
266268
return response_stream
267269

0 commit comments

Comments
 (0)