Skip to content

Commit 2f62d7a

Browse files
authored
Merge pull request #24 from miaow2/develop
Version 1.2.0
2 parents d8c4e31 + d38a1b1 commit 2f62d7a

File tree

8 files changed

+105
-8
lines changed

8 files changed

+105
-8
lines changed

.github/workflows/commit.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
runs-on: ubuntu-latest
3131
strategy:
3232
matrix:
33-
netbox_version: ["v3.5.7"]
33+
netbox_version: ["v3.5.8"]
3434
steps:
3535
- name: Checkout
3636
uses: actions/checkout@v3

docs/changelog.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelog
22

3+
## 1.2.0 (2023-08-23)
4+
5+
* [#20](https://github.com/miaow2/netbox-config-diff/issues/20) Add integration with [netbox-secrets](https://github.com/Onemind-Services-LLC/netbox-secrets) plugin
6+
7+
## 1.1.1 (2023-08-13)
8+
9+
* [#1](https://github.com/miaow2/netbox-config-diff/issues/1) Add tests
10+
11+
## 1.1.0 (2023-08-01)
12+
13+
* [#16](https://github.com/miaow2/netbox-config-diff/issues/16) Add missing and extra config lines
14+
315
## 1.0.0 (2023-07-23)
416

517
* Publish on PyPI.

docs/secrets.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Integration with NetBox secrets plugin
2+
3+
You can store credentials for devices authentification in NetBox secrets [plugin](https://github.com/Onemind-Services-LLC/netbox-secrets).
4+
5+
Read NetBox secrets docs for more info.
6+
7+
In plugin variables define secrets roles for username (`USER_SECRET_ROLE`) and password (`PASSWORD_SECRET_ROLE`).
8+
9+
Default values for this variables are:
10+
11+
```python
12+
PLUGINS_CONFIG = {
13+
"netbox_config_diff": {
14+
"USER_SECRET_ROLE": "Username",
15+
"PASSWORD_SECRET_ROLE": "Password",
16+
},
17+
}
18+
```
19+
20+
Script will find secrets with these roles attached to the device and use them as credentials.
21+
22+
If something goes wrong, then credentials from `PLUGINS_CONFIG` will be used.

mkdocs.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ repo_name: miaow2/netbox-config-diff
55
#strict: true
66
nav:
77
- Home: index.md
8-
- Usage: usage.md
8+
- User Guide:
9+
- Quick Start Guide: usage.md
10+
- Integration with secrets: secrets.md
911
- Screenshots: screenshots.md
1012
- Contributing: contributing.md
1113
- Changelog: changelog.md

netbox_config_diff/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
__author__ = "Artem Kotik"
44
__email__ = "miaow2@yandex.ru"
5-
__version__ = "1.1.1"
5+
__version__ = "1.2.0"
66

77

88
class ConfigDiffConfig(PluginConfig):
@@ -15,6 +15,10 @@ class ConfigDiffConfig(PluginConfig):
1515
base_url = "config-diff"
1616
required_settings = ["USERNAME", "PASSWORD"]
1717
min_version = "3.5.0"
18+
default_settings = {
19+
"USER_SECRET_ROLE": "Username",
20+
"PASSWORD_SECRET_ROLE": "Password",
21+
}
1822

1923

2024
config = ConfigDiffConfig

netbox_config_diff/compliance/base.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,16 @@
1616
from netbox_config_diff.models import ConfigCompliance
1717

1818
from .models import DeviceDataClass
19+
from .secrets import SecretsMixin
1920
from .utils import PLATFORM_MAPPING, exclude_lines, get_unified_diff
2021

2122
try:
22-
from extras.plugins import get_plugin_config
23+
from extras.plugins import get_installed_plugins, get_plugin_config
2324
except ImportError:
24-
from extras.plugins.utils import get_plugin_config
25+
from extras.plugins.utils import get_installed_plugins, get_plugin_config
2526

2627

27-
class ConfigDiffBase:
28+
class ConfigDiffBase(SecretsMixin):
2829
site = ObjectVar(
2930
model=Site,
3031
required=False,
@@ -115,9 +116,13 @@ def log_results(self, device: DeviceDataClass) -> None:
115116
self.log_success(f"{device.name} no diff")
116117

117118
def get_devices_with_rendered_configs(self, devices: Iterable[Device]) -> Iterator[DeviceDataClass]:
118-
username = get_plugin_config("netbox_config_diff", "USERNAME")
119-
password = get_plugin_config("netbox_config_diff", "PASSWORD")
119+
if "netbox_secrets" in get_installed_plugins():
120+
self.get_master_key()
121+
self.user_role = get_plugin_config("netbox_config_diff", "USER_SECRET_ROLE")
122+
self.password_role = get_plugin_config("netbox_config_diff", "PASSWORD_SECRET_ROLE")
120123
for device in devices:
124+
username, password = self.get_credentials(device)
125+
self.log_info(f"{username} {password}")
121126
rendered_config = None
122127
error = None
123128
context_data = device.get_config_context()
@@ -173,3 +178,16 @@ def get_diff(self, devices: list[DeviceDataClass]) -> None:
173178
device.extra = diff_network_config(
174179
cleaned_config, device.rendered_config, PLATFORM_MAPPING[device.platform]
175180
)
181+
182+
def get_credentials(self, device: Device) -> tuple[str, str]:
183+
username = get_plugin_config("netbox_config_diff", "USERNAME")
184+
password = get_plugin_config("netbox_config_diff", "PASSWORD")
185+
if "netbox_secrets" in get_installed_plugins():
186+
if secret := device.secrets.filter(role__name=self.user_role).first():
187+
if value := self.get_secret(secret):
188+
username = value
189+
if secret := device.secrets.filter(role__name=self.password_role).first():
190+
if value := self.get_secret(secret):
191+
password = value
192+
193+
return username, password
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import base64
2+
from typing import TYPE_CHECKING
3+
4+
if TYPE_CHECKING:
5+
from netbox_secrets.models import Secret
6+
7+
8+
class SecretsMixin:
9+
def get_session_key(self) -> None:
10+
if "netbox_secrets_sessionid" in self.request.COOKIES:
11+
self.session_key = base64.b64decode(self.request.COOKIES['netbox_secrets_sessionid'])
12+
elif "HTTP_X_SESSION_KEY" in self.request.META:
13+
self.session_key = base64.b64decode(self.request.META['HTTP_X_SESSION_KEY'])
14+
else:
15+
self.session_key = None
16+
17+
def get_master_key(self) -> None:
18+
try:
19+
from netbox_secrets.models import SessionKey
20+
except ImportError:
21+
return
22+
23+
self.master_key = None
24+
self.get_session_key()
25+
try:
26+
sk = SessionKey.objects.get(userkey__user=self.request.user)
27+
self.master_key = sk.get_master_key(self.session_key)
28+
except Exception as e:
29+
self.log_failure(f"Can't fetch master_key: {str(e)}")
30+
31+
def get_secret(self, secret: "Secret") -> str | None:
32+
try:
33+
secret.decrypt(self.master_key)
34+
except Exception:
35+
return None
36+
return secret.plaintext

netbox_config_diff/navigation.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ def get_add_button(model: str) -> PluginMenuButton:
88
title="Add",
99
icon_class="mdi mdi-plus-thick",
1010
color=ButtonColorChoices.GREEN,
11+
permissions=[f"netbox_config_diff.add_{model}"],
1112
)
1213

1314

@@ -16,10 +17,12 @@ def get_add_button(model: str) -> PluginMenuButton:
1617
link="plugins:netbox_config_diff:platformsetting_list",
1718
link_text="Platform Settings",
1819
buttons=[get_add_button("platformsetting")],
20+
permissions=["netbox_config_diff.view_platformsetting"],
1921
),
2022
PluginMenuItem(
2123
link="plugins:netbox_config_diff:configcompliance_list",
2224
link_text="Config Compliances",
2325
buttons=[],
26+
permissions=["netbox_config_diff.view_configcompliance"],
2427
),
2528
)

0 commit comments

Comments
 (0)