Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add provider class for Nautobot Secrets Functionality #49

Merged
merged 18 commits into from
Jan 24, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 35 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ The plugin behavior can be controlled with the following list of settings.
| dispatcher_mapping | {"newos": "dispatcher.newos"} | None | A dictionary in which the key is a platform slug and the value is the import path of the dispatcher in string format |
| username | ntc | N/A | The username when leveraging the `CredentialsSettingsVars` credential provider. |
| password | password123 | N/A | The password when leveraging the `CredentialsSettingsVars` credential provider. |
| secret | password123 | N/A | The secret password when leveraging the `CredentialsSettingsVars` credential provider, **placeholder only, not currently functioning**. |
| secret | password123 | N/A | The secret password when leveraging the `CredentialsSettingsVars` credential provider.|
| use_config_context | False | Whether to pull Secret Access Type from Config Context.|

Finally, as root, restart Nautobot and the Nautobot worker.

Expand Down Expand Up @@ -176,11 +177,40 @@ class CustomNautobotORMCredentials(NautobotORMCredentials):

You would have to set your `nornir_settings['credentials']` path to your custom class, such as `local_plugin.creds.CustomNautobotORMCredentials`.

Out of the box, users have access to the `nautobot_plugin_nornir.plugins.credentials.settings_vars.CredentialsSettingsVars` and
`nautobot_plugin_nornir.plugins.credentials.env_vars.CredentialsEnvVars` class. This `CredentialsEnvVars` class simply leverages the
environment variables `NAPALM_USERNAME`, `NAPALM_PASSWORD`, and `DEVICE_SECRET`.
Out of the box, users have access to three classes:

> Note: DEVICE_SECRET does not currently work.
- `nautobot_plugin_nornir.plugins.credentials.settings_vars.CredentialsSettingsVars`
- Leverages the username, password, secret that is specified in the plugin configuration.
- `nautobot_plugin_nornir.plugins.credentials.env_vars.CredentialsEnvVars`
- Leverages the environment variables `NAPALM_USERNAME`, `NAPALM_PASSWORD`, and `DEVICE_SECRET`.
- `nautobot_plugin_nornir.plugins.credentials.nautobot_secrets.CredentialsNautobotSecrets`
- Leverages the [Nautobot Secrets Group](https://nautobot.readthedocs.io/en/latest/core-functionality/secrets/#secrets-groups) core functionality. **The default assumes Secrets Group contain secrets with "Access Type" of `Generic`** and expects these secrets to have "Secret Type" of `username`, `password`, and optionally `secret`. The "Access Type" is configurable via the plugin configuration parameter `use_config_context`, which if enabled changes the plugin functionality to pull `['nautobot_plugin_nornir']['secret_access_type']` from each devices config_context.

- Enabling the use of Config Context:
```python
PLUGINS_CONFIG = {
"nautobot_plugin_nornir": {
"use_config_context": True, # <--
"nornir_settings": {
"credentials": "nautobot_plugin_nornir.plugins.credentials.nautobot_secrets.CredentialsNautobotSecrets",
"runner": {
"plugin": "threaded",
"options": {
"num_workers": 20,
},
},
}
jeffkala marked this conversation as resolved.
Show resolved Hide resolved
}
}
```

- Config Context Example:
```yaml
nautobot_plugin_nornir:
secret_access_type: SSH # "GENERIC", "CONSOLE", "GNMI", "HTTP", "NETCONF", "REST", "RESTCONF", "SNMP", "SSH"
```

> For any of these classes, if a "secret" value is not defined, the "password" will also be used as the "secret" value.

The environment variable must be accessible on the web service. This often means simply exporting the environment variable will not
suffice, but instead requiring users to update the `nautobot.service` file, however this will ultimately depend on your own setup. Environment
Expand Down
61 changes: 61 additions & 0 deletions nautobot_plugin_nornir/plugins/credentials/nautobot_secrets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""Credentials class designed to work with Nautobot Secrets Functionality."""

from nautobot.extras.choices import SecretsGroupAccessTypeChoices, SecretsGroupSecretTypeChoices
from nautobot.extras.models.secrets import SecretsGroupAssociation
from nautobot_plugin_nornir.constants import PLUGIN_CFG

from .nautobot_orm import MixinNautobotORMCredentials


def _get_secret_value(secret_type, device_obj):
"""Get value for a secret based on secret type and device.

Args:
secret_type (SecretsGroupSecretTypeChoices): Type of secret to check.
device_obj (dcim.models.Device): Nautobot device object.

Returns:
str: Secret value.
"""
if PLUGIN_CFG.get("use_config_context"):
access_type_str = device_obj.get_config_context()["nautobot_plugin_nornir"]["secret_access_type"].upper()
jeffkala marked this conversation as resolved.
Show resolved Hide resolved
access_type = getattr(SecretsGroupAccessTypeChoices, f"TYPE_{access_type_str}")
else:
access_type = SecretsGroupAccessTypeChoices.TYPE_GENERIC
try:
value = device_obj.secrets_group.get_secret_value(
access_type=access_type,
secret_type=secret_type,
obj=device_obj,
)
except SecretsGroupAssociation.DoesNotExist:
value = None
return value


class CredentialsNautobotSecrets(MixinNautobotORMCredentials):
"""Credentials Class designed to work with Nautobot Secrets Functionality."""

def get_device_creds(self, device):
"""Return the credentials for a given device.

Args:
device (dcim.models.Device): Nautobot device object

Return:
username (string):
password (string):
secret (string):
"""
if device.secrets_group:
self.username = _get_secret_value(
secret_type=SecretsGroupSecretTypeChoices.TYPE_USERNAME, device_obj=device
)
self.password = _get_secret_value(
secret_type=SecretsGroupSecretTypeChoices.TYPE_PASSWORD, device_obj=device
)
self.secret = _get_secret_value(secret_type=SecretsGroupSecretTypeChoices.TYPE_SECRET, device_obj=device)
if not self.secret:
self.secret = self.password
return (self.username, self.password, self.secret)
return (None, None, None)