cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
1843
Views
0
Helpful
9
Replies

Encrypt strings in python MAAPI

tcragg1
Cisco Employee
Cisco Employee

I am working on a service template part of which will generate random strings and store them in an NSO container as tailf:aes-cfb-128-encrypted-string leafs. Is there a simple way inside of python MAAPI to generate the encrypted passwords? When I just pass string values to these leafs it fails with an "item has a bad/wrong type" error.

 

I know that to work with items that are already in ConfDB as tailf:aes-cfb-128-encrypted-string leafs I can use the _ncs.decrypt() function, but I couldn't see an equivalent _ncs.encrypt() function in the docs. Is there a way to encrypt a string directly within python MAAPI to pass to a tailf:aes-cfb-128-encrypted-string leaf, or do we have to use a separate python crypto module to encrypt the string and then send it to the tailf:aes-cfb-128-encrypted-string leaf?

1 Accepted Solution

Accepted Solutions

I realized that another workaround could be to use a small template just for setting the password. 

View solution in original post

9 Replies 9

vleijon
Cisco Employee
Cisco Employee
You should be allowed to just pass the string directly into maapi. If you want to be certain you can prepend $0$ to the string as you set it to indicate that what follows is an unencrypted string.

It appears to work if I run it through a maapi script, but not from directly inside the cb_create method of the python service. The following maapi script works:

 

import random
import string
import ncs
from ncs import _ncs

with ncs.maapi.Maapi() as m:
    with ncs.maapi.Session(m, 'admin', 'python'):
        with m.start_write_trans() as t:
            root = ncs.maagic.get_root(t)
            letters_and_digits = string.ascii_letters + string.digits
            aes_pass = ''.join((random.choice(letters_and_digits) for i in range(8)))
            root.wxc_vpn_snmp.wxc_vpn_customer.create('maapi')
            root.wxc_vpn_snmp.wxc_vpn_customer['maapi'].snmp_aes = aes_pass
            t.apply()

But the following code inside the cb_create method of the service gives me the bad type error:

if not root.wxc_vpn_snmp.wxc_vpn_customer[vrf_name].snmp_aes:
    letters_and_digits = string.ascii_letters + string.digits
    aes_pass = ''.join((random.choice(letters_and_digits) for i in range(8)))
    root.wxc_vpn_snmp.wxc_vpn_customer[vrf_name].snmp_aes = aes_pass

Any idea why this does not work?

Not sure, they ought to work exactly the same way.

It could be a python2 vs python3 difference if the external script is run with python2. Python3 has a tendency to create Iterator-objects instead of collapsing them immediately which can cause trouble. You can try using str() and/or list() to coerce things to the correct type.

I was running the maapi script through python3, and I am using python3 for NSO, so I don't think that can be the issue. I added some extra logging statements in the service and it definitely looks like the aes_pass var is being generated correctly as a string type. Below is the updated code:

if not root.wxc_vpn_snmp.wxc_vpn_customer[vrf_name].snmp_aes:
    letters_and_digits = string.ascii_letters + string.digits
    aes_pass = str(''.join((random.choice(letters_and_digits) for i in range(8))))
    self.log.info('AES pass ', aes_pass, ', type ', type(aes_pass))
    encoded_pass = '$0$' + aes_pass
    self.log.info('encoded pass ', encoded_pass, ', type ', type(encoded_pass))
    root.wxc_vpn_snmp.wxc_vpn_customer[vrf_name].snmp_aes = encoded_pass

 

This results in the following log messaging when attempting a commit dry-run:

 

 

<INFO> 31-Jul-2020::04:17:41.712 wxc-vpn-customer MainThread: - Python 3.6.6 (default, Jan 26 2019, 16:53:05) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]
<INFO> 31-Jul-2020::04:17:41.715 wxc-vpn-customer MainThread: - Starting...
<INFO> 31-Jul-2020::04:17:41.716 wxc-vpn-customer MainThread: - Started
<INFO> 31-Jul-2020::04:17:45.336 wxc-vpn-customer ComponentThread:main: - Main RUNNING
<INFO> 31-Jul-2020::04:17:59.960 wxc-vpn-customer ncs-dp-28246-wxc-vpn-customer:main-1-th-1192342: - Service create(service=/ncs:services/wxc-vpn-customer:wxc-vpn-customer{partner1 cust10})
<INFO> 31-Jul-2020::04:17:59.962 wxc-vpn-customer ncs-dp-28246-wxc-vpn-customer:main-1-th-1192342: - AES pass zS71qF3f, type <class 'str'>
<INFO> 31-Jul-2020::04:17:59.962 wxc-vpn-customer ncs-dp-28246-wxc-vpn-customer:main-1-th-1192342: - encoded pass $0$zS71qF3f, type <class 'str'>
<ERROR> 31-Jul-2020::04:17:59.963 wxc-vpn-customer ncs-dp-28246-wxc-vpn-customer:main-1-th-1192342: - item has a bad/wrong type (5): <<"$0$zS71qF3f">> is not a valid value.
<ERROR> 31-Jul-2020::04:17:59.964 wxc-vpn-customer ncs-dp-28246-wxc-vpn-customer:main-1-th-1192342: - Traceback (most recent call last):
File "/home/tcragg/nso-5.2.3.3/src/ncs/pyapi/ncs/application.py", line 399, in wrapper
pl = fn(self, tctx, root, service, proplist)
File "/home/tcragg/ncs52-run/state/packages-in-use/1/wxc-vpn-customer/python/wxc_vpn_customer/main.py", line 36, in cb_create
root.wxc_vpn_snmp.wxc_vpn_customer[vrf_name].snmp_aes = encoded_pass
File "/home/tcragg/nso-5.2.3.3/src/ncs/pyapi/ncs/maagic.py", line 483, in __setattr__
child.set_value(value)
File "/home/tcragg/nso-5.2.3.3/src/ncs/pyapi/ncs/maagic.py", line 956, in set_value
self._backend._set_elem(value, self._path)
File "/home/tcragg/nso-5.2.3.3/src/ncs/pyapi/ncs/maagic.py", line 124, in _set_elem
self.set_elem(value, path)
File "/home/tcragg/nso-5.2.3.3/src/ncs/pyapi/ncs/maapi.py", line 1202, in proxy
return real(self2.maapi, self2.th, *args, **kwargs)
File "/home/tcragg/nso-5.2.3.3/src/ncs/pyapi/ncs/tm.py", line 12, in wrapper
return fn(*args, **kwargs)
File "/home/tcragg/nso-5.2.3.3/src/ncs/pyapi/ncs/maapi.py", line 667, in shared_set_elem
_tm.maapi.shared_set_elem(self.msock, th, value, flags, path)
_ncs.error.Error: item has a bad/wrong type (5): <<"$0$zS71qF3f">> is not a valid value.

From the log messages, it looks like it is just refusing to accept a string value for this leaf.

 

I am running NSO 5.2.3.3 if that makes a difference.

 

Hello,

This seems to be a limitation in maagic, currently.

It seems if you want to use maagic to set the value of an encrypted leaf (in a service callback), you will have to provide the 'already encrypted value' (ie., something that starts with $8$, in this case).

It works outside a service. You can also use plain maapi, and do maapi.shared_set_elem() inside the service callback. You can do something like

        m = maapi.Maapi()
        th = m.attach(tctx)
        th.shared_set_elem('dummypass', '<path_to_encrypted_leaf>')

where 'tctx' is the transaction context you receive as a parameter to your service callback.

It just doesn't work in maagic, as of now.

Please create a support request, so that this can be fixed.

Thanks,

Ram

I realized that another workaround could be to use a small template just for setting the password. 

Separating the credentials into an XML template appears to allow me to pass the unencrypted credentials correctly, and is also a simpler approach than messing around with directly accessing MAAPI from inside the cb_create() method, so I will use this approach if I need to pass plain text to an encrypted leaf going forward.

Thanks Ram.

 

I am not very familiar with the shared_set_elem command. What format does it expect the path to the element to be in? I assumed it would just be XPATH, but when I try "th.shared_set_elem(aes_pass, "/wxc-vpn-snmp/wxc-vpn-customer[vrf_name='maapi']/snmp-aes")", it gives me a bad path error. This should be a valid XPATH as it is pointing to an existing list entry:

admin_tcragg@ncs# show running-config wxc-vpn-snmp | display xpath
/wxc-vpn-snmp/wxc-vpn-customer[vrf_name='maapi']/snmp-aes $8$DMo1By0HjgSiPRbz3FbBiuLJNe3aN2r5HjKNSutsk+o=

It takes a keypath, that is something like /wxc-vpn-snmp/wxc-vpn-customer{maapi} /snmp-aes