├── README.md ├── load-dir └── .gitignore ├── package-meta-data.xml ├── python └── ipython-superuser.py └── src ├── Makefile └── cli ├── ipython-superuser-j.cli └── ipython-superuser.cli /README.md: -------------------------------------------------------------------------------- 1 | # IPython-superuser 2 | 3 | Your IPython NSO hood-opener with superuser powers 4 | 5 | # Purpose 6 | 7 | IPython-superuser grants any NSO CLI Operator superuser access to an 8 | ipython environment which can see and modify anything in the 9 | Operator's transaction. 10 | 11 | # SECURITY WARNING 12 | 13 | By installing this package, any operator can become 'superuser' and 14 | read and write any part of the configuration, including decryption of 15 | encrypted data such as passwords, read any non-configuration data, 16 | invoke any action. The operator may also gain full access to the 17 | underlaying operating system with the same privileges as NSO. 18 | 19 | You can use traditional NSO NACM access control rules to lock down who 20 | can execute the 'ipython' command. Unless specifically configured, all 21 | operators will have access by default. 22 | 23 | **LET ME STRESS AGAIN:** __Anyone who can execute this command will 24 | leave the realm of NACM and have full control over all data and 25 | actions NSO can reach, including encrypted data. NACM rules will 26 | not apply as long as the operator stays in the IPython 27 | environment. From within IPython, the operator will also have the 28 | same acess to the underlaying operating system as NSO itself. An 29 | operator could easily start up a traditional shell and run any 30 | OS-level commands that NSO has privilege to execute. If NSO runs 31 | as root, that means access to all commands and files in the 32 | underlaying operating system.__ 33 | 34 | # Dependencies 35 | 36 | In order to run all of the functionality, you will need to have (in 37 | the path) the following components. If something is missing, your most 38 | important use cases may still work, but some tools will not. 39 | 40 | * NSO 4.2+ 41 | * Python 2.7+ or 3+ with ipython support 42 | 43 | # Build instructions 44 | 45 | Normal NSO package build: 46 | 47 | make -C packages/ipython-superuser/src/ clean all 48 | 49 | # Usage examples 50 | 51 | ## Listing the device names 52 | 53 | Just type ipython on an operational or configuration prompt to get started. 54 | 55 | JLINDBLA-M-J8L9# ipython 56 | Python 2.7.11 (default, Jan 22 2016, 08:29:18) 57 | Type "copyright", "credits" or "license" for more information. 58 | 59 | IPython 5.1.0 -- An enhanced Interactive Python. 60 | ? -> Introduction and overview of IPython's features. 61 | %quickref -> Quick reference. 62 | help -> Python's own help system. 63 | object? -> Details about 'object', use 'object??' for extra details. 64 | +-----------------------------------------------------------------------------+ 65 | | You may reference the current transaction maagic YANG root object as 'root' | 66 | | E.g. In [1]: for dev in root.devices.device: | 67 | | ...: print dev.name | 68 | +-----------------------------------------------------------------------------+ 69 | 70 | Hit tab as you type past root. to complete the name, or to get a menu 71 | of available matching choices. 72 | 73 | In [1]: for dev in root.devices.device: 74 | ...: print dev.name 75 | ...: 76 | ce0 77 | ce1 78 | ce2 79 | ce3 80 | 81 | ## Returning to NSO CLI 82 | 83 | From operational mode, no assignments will be possible. When you're 84 | done, exit to get back to NSO 85 | 86 | In [2]: exit 87 | JLINDBLA-M-J8L9# 88 | 89 | ## Running a query 90 | 91 | A few python variables are available in your session. The most 92 | important one is usually `root`, which is the MAAGIC root object. It 93 | represents the root of the YANG model. As seen above, you can use it to 94 | navigate anywhere in the YANG. 95 | 96 | If a keypath was given to the ipython cli command, a MAAGIC object named 97 | 'path' will be available bound the the keypath provided as input. 98 | 99 | The current transaction object is available as `trans`. You can use 100 | this object to run a query: 101 | 102 | In [32]: trans.query_start(expr="/devices/device[port='8300']", 103 | context_node='/', chunk_size=10, initial_offset=0, result_as=1, 104 | select=['name'], sort=['name']) 105 | Out[32]: 3957 106 | 107 | In [33]: for res in maapi.query_result(Out[32]): 108 | ...: print res 109 | ...: 110 | ['/ncs:devices/device{p3}/name'] 111 | ['/ncs:devices/device{pe2}/name'] 112 | ['/ncs:devices/device{xr-local}/name'] 113 | 114 | ## Decrypting passwords 115 | 116 | Or decrypt a password. Here we also need to use the `maapi` and `ncs` 117 | objects to install a copy of the NSO crypto keys in the IPython 118 | environment and call the `decrypt` method. 119 | 120 | In [1]: root.devices.authgroups.group.keys() 121 | Out[1]: 122 | [Key values = [<_ncs.Value type=C_BUF(5) value='default'>], 123 | Key values = [<_ncs.Value type=C_BUF(5) value='vagrant'>], 124 | Key values = [<_ncs.Value type=C_BUF(5) value='zenoss'>]] 125 | 126 | In [2]: root.devices.authgroups.group['default'].umap 127 | Out[2]: List name=umap tag=2113235867 128 | 129 | In [3]: root.devices.authgroups.group['default'].umap.keys() 130 | Out[3]: 131 | [Key values = [<_ncs.Value type=C_BUF(5) value='admin'>], 132 | Key values = [<_ncs.Value type=C_BUF(5) value='oper'>]] 133 | 134 | In [4]: root.devices.authgroups.group['default'].umap['admin'].remote_password 135 | Out[4]: '$8$o8y+hdI1DWjvnlPH0XHaQKAvWxofjrba1rgb62IKZ/E=' 136 | 137 | In [5]: maapi.install_crypto_keys() 138 | 139 | In [6]: ncs._ncs.decrypt(Out[4]) 140 | Out[6]: 'admin' 141 | 142 | ## Syncing from all cisco-ios devices 143 | 144 | Running sync-from with all devices that run the cisco-ios CLI NED. 145 | 146 | In [22]: for dev in root.devices.device: 147 | ...: if dev.device_type.cli.exists() and dev.device_type.cli.ned_id == 'cisco-ios': 148 | ...: print "Syncing from %s"%dev.name 149 | ...: dev.sync_from() 150 | ...: 151 | 152 | ## Changing host name on IOS device 153 | 154 | Let's check and configure the ce0 hostname. 155 | 156 | In [1]: root.devices.device['ce0'].config.ios__hostname 157 | 158 | Ok, none set. 159 | 160 | In [2]: root.devices.device['ce0'].config.ios__hostname = 'ce0' 161 | 162 | In [3]: root.devices.device['ce0'].config.ios__hostname 163 | Out[3]: 'ce0' 164 | 165 | Now set. Return to NSO CLI to commit. 166 | 167 | In [4]: exit 168 | JLINDBLA-M-J8L9(config)# show c 169 | devices device ce0 170 | config 171 | ios:hostname ce0 172 | ! 173 | ! 174 | JLINDBLA-M-J8L9(config)# commit 175 | 176 | ## Configuring BGP logging on all IOS-XR NETCONF devices 177 | 178 | In [3]: for dev in root.devices.device: 179 | ...: if 'http://cisco.com/ns/yang/Cisco-IOS-XR-ipv4-bgp-cfg' in [key[0] for key in dev.capability.keys()]: 180 | ...: print "Enabling BGP neighbor best path logging on %s"%dev.name 181 | ...: dev.config.ipv4_bgp_cfg__bgp.instance.create('default').instance_as.create('1').four_byte_as.create('1').default_vrf.ipv4_bgp_cfg__global.ne 182 | ...: ighbor_logging_detail.create() 183 | ...: 184 | Enabling BGP neighbor best path logging on p3 185 | Enabling BGP neighbor best path logging on pe2 186 | Enabling BGP neighbor best path logging on xr-local 187 | 188 | In [4]: exit 189 | JLINDBLA-M-J8L9(config)# show c 190 | devices device p3 191 | config 192 | ipv4-bgp-cfg:bgp instance default 193 | instance-as 1 194 | four-byte-as 1 195 | default-vrf global neighbor-logging-detail 196 | ! 197 | ! 198 | ! 199 | ! 200 | ! 201 | devices device pe2 202 | config 203 | ipv4-bgp-cfg:bgp instance default 204 | instance-as 1 205 | four-byte-as 1 206 | default-vrf global neighbor-logging-detail 207 | ! 208 | ! 209 | ! 210 | ! 211 | ! 212 | devices device xr-local 213 | config 214 | ipv4-bgp-cfg:bgp instance default 215 | instance-as 1 216 | four-byte-as 1 217 | default-vrf global neighbor-logging-detail 218 | ! 219 | ! 220 | ! 221 | ! 222 | ! 223 | JLINDBLA-M-J8L9(config)# commit 224 | 225 | # Troubleshooting 226 | 227 | ## ipython-superuser.py not found 228 | 229 | You see this message: 230 | 231 | [TerminalIPythonApp] WARNING | File not found: u'packages/ipython-superuser/python/ipython-superuser.py' 232 | 233 | This means ipython-superuser can't find it's initialization 234 | file. Typically this would happen if NSO wasn't started from the 235 | runtime directory (where the packages/ directory resides). In this 236 | case you may need to update the ipython-superuser.cli file with the 237 | correce path, rebuild the package, reload packages and try again. 238 | 239 | ## not writable 240 | 241 | You see this message: 242 | 243 | Error: item is not writable (4): 244 | 245 | This means you're trying to modify the configuration, but started the 246 | ipython session from operational mode. Exit out of IPython, enter 247 | config mode and try again. But don't despair, the up-arrow command 248 | history is preserved. 249 | 250 | # Contact 251 | 252 | Contact Jan Lindblad with any suggestions or 253 | comments. 254 | -------------------------------------------------------------------------------- /load-dir/.gitignore: -------------------------------------------------------------------------------- 1 | *.ccl 2 | -------------------------------------------------------------------------------- /package-meta-data.xml: -------------------------------------------------------------------------------- 1 | 2 | ipython-superuser 3 | 1.0 4 | IPython-superuser grants any NSO CLI Operator superuser 5 | access to an ipython environment which can see and modify anything 6 | in the Operator's transaction. WARNING: By installing this package, 7 | any operator can become 'superuser' and read and write any part of 8 | the configuration, including decryption of encrypted data such as 9 | passwords, read any non-configuration data, invoke any action. The 10 | operator may also gain full access to the underlaying operating 11 | system with the same privileges as NSO. 12 | 4.2 13 | 14 | -------------------------------------------------------------------------------- /python/ipython-superuser.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import sys 4 | 5 | time.sleep(1) 6 | 7 | import ncs 8 | ncs_trans_id=int(os.environ['NCS_MAAPI_THANDLE']) 9 | ncs_sess_id=int(os.environ['NCS_MAAPI_USID']) 10 | maapi=ncs.maapi.Maapi() 11 | trans=maapi.attach(ncs_trans_id) 12 | root=ncs.maagic.get_root(trans) 13 | if len(sys.argv) > 1: 14 | path = ncs.maagic.get_node(trans, sys.argv[1]) 15 | 16 | print("+-----------------------------------------------------------------------------+") 17 | print("| You may reference the current transaction maagic YANG root object as 'root' |") 18 | print("| E.g. In [1]: for dev in root.devices.device: |") 19 | print("| ...: print dev.name |") 20 | print("| If a keypath was given to the ipython cli command, you may reference |") 21 | print("| the maagic YANG path object as 'path', E.g: |") 22 | print("| JLINDBLA-M-J8L9# ipython devices device |") 23 | print("| In [1]: for dev in path: |") 24 | print("| ...: print dev.name |") 25 | print("+-----------------------------------------------------------------------------+") 26 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | all: ccl 2 | 3 | # Include standard NCS examples build definitions and rules 4 | include $(NCS_DIR)/src/ncs/build/include.ncs.mk 5 | 6 | ccl: ../load-dir/ipython-superuser.ccl \ 7 | ../load-dir/ipython-superuser-j.ccl 8 | 9 | ../load-dir/%.ccl: cli/%.cli 10 | $(NCSC) -c -o $@ $< 11 | 12 | clean: 13 | rm -rf ../load-dir/*.ccl 14 | -------------------------------------------------------------------------------- /src/cli/ipython-superuser-j.cli: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Run IPython 5 | Run IPython 6 | 7 | 8 | ipython 9 | -i packages/ipython-superuser/python/ipython-superuser.py 10 | 11 | user 12 | user 13 | 14 | 15 | 16 | 17 | 18 | exist 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Run IPython 27 | Run IPython 28 | 29 | 30 | ipython 31 | -i packages/ipython-superuser/python/ipython-superuser.py 32 | 33 | user 34 | user 35 | 36 | 37 | 38 | 39 | 40 | exist 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/cli/ipython-superuser.cli: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Run IPython 5 | Run IPython 6 | 7 | 8 | ipython 9 | -i packages/ipython-superuser/python/ipython-superuser.py 10 | 11 | user 12 | user 13 | 14 | 15 | 16 | 17 | 18 | exist 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Run IPython 27 | Run IPython 28 | 29 | 30 | ipython 31 | -i packages/ipython-superuser/python/ipython-superuser.py 32 | 33 | user 34 | user 35 | 36 | 37 | 38 | 39 | 40 | exist 41 | 42 | 43 | 44 | 45 | 46 | 47 | --------------------------------------------------------------------------------