It is best practice to avoid storing your secrets (e.g., passwords and shared keys) in plain text, either on NSO or on the device. In NSO, we support multiple encrypted data types that are encrypted using a local key. Similarly, many devices such as Cisco IOS XE support automatically encrypting all passwords stored on the device. On Cisco IOS, this can be done using commands like these:
key config-key password-encryption SUPERSECRET
service password-encryption
password encryption aes
which makes the system automatically encrypt all passwords using the key SUPERSECRET
and show them encrypted in the output of show running
.
Naturally, for security reasons, NSO, in general, has no way of encrypting/decrypting passwords with the secret key on the device. If nothing is done about this, we will become out of sync once we write secrets to the device. Looking at just the cisco-ios
NED, there are over 500 paths that contain such secrets.
Luckily, many of our CLI NEDs support various levels of security and secrets management. For example, the NED handles auto-encryption by reading back auto-encrypted values immediately after writing them and storing the encrypted value in a special secrets
table. This allows the NED to replace any future occurrences of the encrypted value with the known plaintext from NSO.
This re-reading handles the device-side encryption, but passwords are still unencrypted in NSO. So, in addition to managing device-side encryption, we support using NSO-encrypted strings instead of plaintext passwords in the NSO data model.
NED support
The following NED has at least some support for secrets: cisco-asa
, cisco-ios
, cisco-iosxr
, cisco-nx
, cisco-staros
, and huawei-vrp
. This document aims to be generic but will use cisco-ios
for the examples. Please look at the README
file for each NED for specific details; some NEDs have additional ned-settings
or features available.
Handling auto-encryption
Let us say that we have password-encryption on and we want to write a new user to our device:
admin@ncs(config)# devices device ios0 config username newuser password 0 magic
admin@ncs(config-config)# commit
this will be automatically encrypted by the device
Router#show running-config | sec userna
username newuser password 6 _Ab[PDCO[fQDJhDfMIciONMedifAAB
But the secrets management will store this new encrypted value in our secrets
table:
admin@ncs# show devices device ios0 ned-settings secrets
ID ENCRYPTED REGEX
---------------------------------------------------------------------------------
ios:username(newuser)/password/secret 6 _Ab[PDCO[fQDJhDfMIciONMedifAAB
which means that no compare-config
or sync-from
will update our CDB. In fact, we can still see the unencrypted value in the device tree:
admin@ncs# show running-config devices device ios0 config username
devices device ios0
config
username admin password 6 S]iVZERdHTgVdf_KVQYOMXQRR^QHM^
username cisco privilege 15 password 6 "Y]i`\ZegLUiB[EMPYUAKhOBK]VRAAB"
username newuser password 0 magic
!
!
Increasing security with NSO-side encryption
We have two alternatives, we can either manually encrypt our values using one of the NSO-encrypted types (e.g., aes-256-cfb-128-encrypted-string
) and set them to the tree, or we can recompile the NED to always encrypt secrets.
Setting encrypted value
Let us say we know that the NSO-encrypted string $9$T963R76+wgaQuZCtcGC/Nreo75FigP+znmOln8XDFK0=
(admin
), we can then set it in the device tree as normal
admin@ncs(config)# devices device ios0 config username newuser2 password 0 $9$T963R76+wgaQuZCtcGC/Nreo75FigP+znmOln8XDFK0=
admin@ncs(config-config)# commit
when committing this value, it will be decrypted by the NED, and the plaintext will be sent to the device. However, for security reasons, the encrypted value will be used in the device traces.
Unlike the previous example, the plaintext is not visible in the device tree:
admin@ncs# show running-config devices device ios0 config username
devices device ios0
config
username admin password 6 S]iVZERdHTgVdf_KVQYOMXQRR^QHM^
username cisco privilege 15 password 6 "Y]i`\ZegLUiB[EMPYUAKhOBK]VRAAB"
username newuser password 0 magic
username newuser2 password 0 $9$T963R76+wgaQuZCtcGC/Nreo75FigP+znmOln8XDFK0=
!
!
On the device side, this plaintext value is, of course, encrypted with the device key, and just as before, we store it in our secrets
table:
admin@ncs# show devices device ios0 ned-settings secrets
ID ENCRYPTED REGEX
---------------------------------------------------------------------------------
ios:username(newuser)/password/secret 6 _Ab[PDCO[fQDJhDfMIciONMedifAAB
ios:username(newuser2)/password/secret 6 XTXfOVUcYDZKd`FBH\S]XEJ_FcIAAB
We can see that this corresponds to the value set on the device:
Router#show running-config | sec userna
username admin password 6 S]iVZERdHTgVdf_KVQYOMXQRR^QHM^
username cisco privilege 15 password 6 Y]i`\ZegLUiB[EMPYUAKhOBK]VRAAB
username newuser password 6 _Ab[PDCO[fQDJhDfMIciONMedifAAB
username newuser2 password 6 XTXfOVUcYDZKd`FBH\S]XEJ_FcIAAB
Note that we do not have entries for admin
or cisco
because these values were set directly on NSO and not on the device, we do not know the corresponding plaintext value, and they are handled entirely as encrypted values.
Auto-encrypting passwords in NSO
You can rebuild your NED with an encrypted type for secrets using a command like NEDCOM_SECRET_TYPE="tailf:aes-cfb-128-encrypted-string" make -C src/
or by setting NED_EXTRA_BUILDFLAGS ?= NEDCOM_SECRET_TYPE=tailf:aes-cfb-128-encrypted-string
in the Makefile
. Doing this means that even if the input to a password is a plaintext string, NSO will always encrypt it, and you will never see plaintext secrets in the device tree.
NOTE: This will work only for leafs in the YANG model which has the type NEDCOM_SECRET_TYPE.
If we reload our example with the new NED all of the secrets are now encrypted:
admin@ncs# show running-config devices device ios0 config username
devices device ios0
config
username admin password 6 $8$LkeTpiWvR2fm0P0DK0FXPg61LAOw5TACkB1y7FYvZIYYNE2CqMyQOwZ7uDeod7oR
username cisco privilege 15 password 6 $8$0D4JYVCvfoLqu5u77OImrdzWuP4hp9HzcAXbQPAoQUmNNWjF0VoOPVeaPRfoRqrI
username newuser password 0 $8$IpJZaKg3HZ+7JMdhmcVlbdw2P+htNdThDuYKdldDAqM=
username newuser2 password 0 "$8$BVzY1FLE47Wum5WXokAVZ3UqaeJQt4s7ksGyiWKOLxGZIUrhp92KqBG4R2zINyMFl+L71TOk\naT8u3/l4L/p4Xg=="
!
!
and if we create yet another user we get the desired result:
admin@ncs(config)# devices device ios0 config username newuser3 password 0 automatic
admin@ncs(config-config)# commit dry-run outformat native
native {
device {
name ios0
data username newuser3 password 0 $8$s3EN60QdR5flGa1cv0K3/50iHX4wBcRkjrhFaok7ALg=
}
}
admin@ncs(config-config)# commit
Commit complete.
admin@ncs(config-config)# end
admin@ncs# show running-config devices device ios0 config username newuser3
devices device ios0
config
username newuser3 password 0 $8$s3EN60QdR5flGa1cv0K3/50iHX4wBcRkjrhFaok7ALg=
!
!
admin@ncs# show devices device ios0 ned-settings secrets
ID ENCRYPTED REGEX
---------------------------------------------------------------------------------------
ios:username(newuser)/password/secret 6 _Ab[PDCO[fQDJhDfMIciONMedifAAB
ios:username(newuser2)/password/secret 6 XTXfOVUcYDZKd`FBH\S]XEJ_FcIAAB
ios:username(newuser3)/password/secret 6 QFVNW\__Pc[gDG_Fi_ccEQQWHA[DIHJhTAAB