Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .github/workflows/commit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
netbox_version: ["v3.5.7"]
netbox_version: ["v3.5.8"]
steps:
- name: Checkout
uses: actions/checkout@v3
Expand Down
12 changes: 12 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## 1.2.0 (2023-08-23)

* [#20](https://github.com/miaow2/netbox-config-diff/issues/20) Add integration with [netbox-secrets](https://github.com/Onemind-Services-LLC/netbox-secrets) plugin

## 1.1.1 (2023-08-13)

* [#1](https://github.com/miaow2/netbox-config-diff/issues/1) Add tests

## 1.1.0 (2023-08-01)

* [#16](https://github.com/miaow2/netbox-config-diff/issues/16) Add missing and extra config lines

## 1.0.0 (2023-07-23)

* Publish on PyPI.
Expand Down
22 changes: 22 additions & 0 deletions docs/secrets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Integration with NetBox secrets plugin

You can store credentials for devices authentification in NetBox secrets [plugin](https://github.com/Onemind-Services-LLC/netbox-secrets).

Read NetBox secrets docs for more info.

In plugin variables define secrets roles for username (`USER_SECRET_ROLE`) and password (`PASSWORD_SECRET_ROLE`).

Default values for this variables are:

```python
PLUGINS_CONFIG = {
"netbox_config_diff": {
"USER_SECRET_ROLE": "Username",
"PASSWORD_SECRET_ROLE": "Password",
},
}
```

Script will find secrets with these roles attached to the device and use them as credentials.

If something goes wrong, then credentials from `PLUGINS_CONFIG` will be used.
4 changes: 3 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ repo_name: miaow2/netbox-config-diff
#strict: true
nav:
- Home: index.md
- Usage: usage.md
- User Guide:
- Quick Start Guide: usage.md
- Integration with secrets: secrets.md
- Screenshots: screenshots.md
- Contributing: contributing.md
- Changelog: changelog.md
Expand Down
6 changes: 5 additions & 1 deletion netbox_config_diff/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

__author__ = "Artem Kotik"
__email__ = "miaow2@yandex.ru"
__version__ = "1.1.1"
__version__ = "1.2.0"


class ConfigDiffConfig(PluginConfig):
Expand All @@ -15,6 +15,10 @@ class ConfigDiffConfig(PluginConfig):
base_url = "config-diff"
required_settings = ["USERNAME", "PASSWORD"]
min_version = "3.5.0"
default_settings = {
"USER_SECRET_ROLE": "Username",
"PASSWORD_SECRET_ROLE": "Password",
}


config = ConfigDiffConfig
28 changes: 23 additions & 5 deletions netbox_config_diff/compliance/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@
from netbox_config_diff.models import ConfigCompliance

from .models import DeviceDataClass
from .secrets import SecretsMixin
from .utils import PLATFORM_MAPPING, exclude_lines, get_unified_diff

try:
from extras.plugins import get_plugin_config
from extras.plugins import get_installed_plugins, get_plugin_config
except ImportError:
from extras.plugins.utils import get_plugin_config
from extras.plugins.utils import get_installed_plugins, get_plugin_config


class ConfigDiffBase:
class ConfigDiffBase(SecretsMixin):
site = ObjectVar(
model=Site,
required=False,
Expand Down Expand Up @@ -115,9 +116,13 @@ def log_results(self, device: DeviceDataClass) -> None:
self.log_success(f"{device.name} no diff")

def get_devices_with_rendered_configs(self, devices: Iterable[Device]) -> Iterator[DeviceDataClass]:
username = get_plugin_config("netbox_config_diff", "USERNAME")
password = get_plugin_config("netbox_config_diff", "PASSWORD")
if "netbox_secrets" in get_installed_plugins():
self.get_master_key()
self.user_role = get_plugin_config("netbox_config_diff", "USER_SECRET_ROLE")
self.password_role = get_plugin_config("netbox_config_diff", "PASSWORD_SECRET_ROLE")
for device in devices:
username, password = self.get_credentials(device)
self.log_info(f"{username} {password}")
rendered_config = None
error = None
context_data = device.get_config_context()
Expand Down Expand Up @@ -173,3 +178,16 @@ def get_diff(self, devices: list[DeviceDataClass]) -> None:
device.extra = diff_network_config(
cleaned_config, device.rendered_config, PLATFORM_MAPPING[device.platform]
)

def get_credentials(self, device: Device) -> tuple[str, str]:
username = get_plugin_config("netbox_config_diff", "USERNAME")
password = get_plugin_config("netbox_config_diff", "PASSWORD")
if "netbox_secrets" in get_installed_plugins():
if secret := device.secrets.filter(role__name=self.user_role).first():
if value := self.get_secret(secret):
username = value
if secret := device.secrets.filter(role__name=self.password_role).first():
if value := self.get_secret(secret):
password = value

return username, password
36 changes: 36 additions & 0 deletions netbox_config_diff/compliance/secrets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import base64
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from netbox_secrets.models import Secret


class SecretsMixin:
def get_session_key(self) -> None:
if "netbox_secrets_sessionid" in self.request.COOKIES:
self.session_key = base64.b64decode(self.request.COOKIES['netbox_secrets_sessionid'])
elif "HTTP_X_SESSION_KEY" in self.request.META:
self.session_key = base64.b64decode(self.request.META['HTTP_X_SESSION_KEY'])
else:
self.session_key = None

def get_master_key(self) -> None:
try:
from netbox_secrets.models import SessionKey
except ImportError:
return

self.master_key = None
self.get_session_key()
try:
sk = SessionKey.objects.get(userkey__user=self.request.user)
self.master_key = sk.get_master_key(self.session_key)
except Exception as e:
self.log_failure(f"Can't fetch master_key: {str(e)}")

def get_secret(self, secret: "Secret") -> str | None:
try:
secret.decrypt(self.master_key)
except Exception:
return None
return secret.plaintext
3 changes: 3 additions & 0 deletions netbox_config_diff/navigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ def get_add_button(model: str) -> PluginMenuButton:
title="Add",
icon_class="mdi mdi-plus-thick",
color=ButtonColorChoices.GREEN,
permissions=[f"netbox_config_diff.add_{model}"],
)


Expand All @@ -16,10 +17,12 @@ def get_add_button(model: str) -> PluginMenuButton:
link="plugins:netbox_config_diff:platformsetting_list",
link_text="Platform Settings",
buttons=[get_add_button("platformsetting")],
permissions=["netbox_config_diff.view_platformsetting"],
),
PluginMenuItem(
link="plugins:netbox_config_diff:configcompliance_list",
link_text="Config Compliances",
buttons=[],
permissions=["netbox_config_diff.view_configcompliance"],
),
)