Skip to content
Open
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
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Netbox Plugin DDDC

Even if there are tools in Malcolm and [Netbox itself](https://docs.netboxlabs.com/netbox-extensions/diode-overview/) getting data into Netbox, this data should be standardized. This is done by this plugin, which contains the source code for the BSI Project 507 TP2. The DDDC plugin can receive input data from various sources, supports the processing and approval of this data in order to build a standardized device database within Netbox.\\
The main features are further developed in the repository [String-Atlas](https://github.com/DINA-community/String-Atlas). This processes the data before it is placed in the Netbox framework. This ensures that the data is adapted to support IT security management tasks such as device management, vulnerability management and patch management.
Even if there are tools in Malcolm and [NetBox itself](https://docs.netboxlabs.com/netbox-extensions/diode-overview/) getting data into NetBox, this data should be standardized. This is done by this plugin, which contains the source code for the BSI Project 507 TP2. The DDDC plugin can receive input data from various sources, supports the processing and approval of this data in order to build a standardized device database within NetBox.\\
The main features are further developed in the repository [String-Atlas](https://github.com/DINA-community/String-Atlas). This processes the data before it is placed in the NetBox framework. This ensures that the data is adapted to support IT security management tasks such as device management, vulnerability management and patch management.

In addition to the plugin code, this repository contains additional files for the community-driven [Docker image](https://github.com/netbox-community/netbox-docker) integrating the DDDC Plugin in development mode. This is primarily used for test purposes for the CI/CD pipeline and can be used for testing the plugin within an exemplary Netbox environment.
In addition to the plugin code, this repository contains additional files for the community-driven [Docker image](https://github.com/netbox-community/netbox-docker) integrating the DDDC Plugin in development mode. This is primarily used for test purposes for the CI/CD pipeline and can be used for testing the plugin within an exemplary NetBox environment.

## Installation of the DDDC Plugin

As the DDDC plugin is a standard Netbox plugin, it can be installed according to the [Netbox documentation](https://docs.netbox.dev/en/stable/plugins/#installing-plugins).
This plugin is compatible with Netbox version 4.2.7 and ensured by the docker file.

Additionally, this repository contains files from the community-driven Docker image to set up Netbox, along with all its dependencies, such as a PostgreSQL database. Please note: This is not an installation for a production environment, as it uses default passwords and API keys as specified in the project's files. Furthermore, this installation sets up Netbox in 'developer mode', which means that the user will receive detailed information in case of an exception. This is very useful for alpha and beta testing, which is why this installation option is described below:
Additionally, this repository contains files from the community-driven Docker image to set up NetBox, along with all its dependencies, such as a PostgreSQL database. Please note: This is not an installation for a production environment, as it uses default passwords and API keys as specified in the project's files. Furthermore, this installation sets up NetBox in 'developer mode', which means that the user will receive detailed information in case of an exception. This is very useful for alpha and beta testing, which is why this installation option is described below:

## Adding the plugin to an existing netbox-docker installation

Expand Down Expand Up @@ -78,7 +78,7 @@ Recommendation: Install docker with the Compose v2 already integrated into the D

To check the version installed on your system run `docker --version` and `docker compose version`.

After the installation, Netbox is available at [http://127.0.0.1:8000](http://127.0.0.1:8000).
After the installation, NetBox is available at [http://127.0.0.1:8000](http://127.0.0.1:8000).
Therefore, for simplicity, a web browser should be available on the installed system.

### Installation for developing and testing purposes
Expand Down Expand Up @@ -123,25 +123,25 @@ In theory, you can add an alternative security token in the file netbox.env by a
SUPERUSER_API_TOKEN=<Token>
```

However, an important aspect of an installation in a production environment is the creation of users, tokens, and their permissions. This must be done for each Netbox installation separately and in accordance with the specific requirements in place.
However, an important aspect of an installation in a production environment is the creation of users, tokens, and their permissions. This must be done for each NetBox installation separately and in accordance with the specific requirements in place.

### Testing

The unit tests of netbox can be executed via `./docker-ci/test.sh`.
The unit tests of NetBox can be executed via `./docker-ci/test.sh`.

## Help

This section contains links for familiarizing yourself with Django, Netbox, and plugins.
This section contains links for familiarizing yourself with Django, NetBox, and plugins.

### General

- Installation of NetBox as a standalone, self-hosted application: <https://docs.netbox.dev/en/stable/installation/>
- Community driven Docker image for netbox: <https://github.com/netbox-community/netbox-docker>
- Community driven Docker image for NetBox: <https://github.com/netbox-community/netbox-docker>
- Using Netbox Plugins in Docker: <https://github.com/netbox-community/netbox-docker/wiki/Using-Netbox-Plugins>

### Development

- Official plugin development documentation of NetBox: <https://docs.netbox.dev/en/stable/plugins/development/>
- NetbBox plugin development Tutorial: <https://github.com/netbox-community/netbox-plugin-tutorial>
- NetBox plugin development Tutorial: <https://github.com/netbox-community/netbox-plugin-tutorial>
- Setting up a development environment with Docker for NetBox plugins: <https://github.com/netbox-community/netbox-docker/discussions/746>
- django-table2 Documentation used by the Plugin and Netbox: <https://django-tables2.readthedocs.io/en/latest/>
- django-table2 Documentation used by the Plugin and NetBox: <https://django-tables2.readthedocs.io/en/latest/>
4 changes: 2 additions & 2 deletions d3c/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Meta:
fields = ('id', 'device', 'url', 'source', 'confidence',
'description', 'device_role', 'serial_number', 'device_name', 'status', 'site', 'rack', 'location',
'device_type', 'serial_number', 'device_role', 'is_safety_critical',
'ip_address', 'mac_address', 'transport_protocol', 'application_protocol', 'port',
'ip_address', 'ip_netmask', 'mac_address', 'transport_protocol', 'application_protocol', 'port',
'is_router', 'manufacturer', 'oui', 'device_family',
'article_number', 'part_number', 'hardware_version', 'hardware_cpe', 'software_name',
'is_firmware', 'version', 'exposure', 'has_predicted_device', 'predicted_device')
Expand Down Expand Up @@ -69,7 +69,7 @@ class CommunicationFindingSerializer(NetBoxModelSerializer):

class Meta:
model = CommunicationFinding
fields = ('id', 'url', 'source', 'source_ip', 'destination_ip', 'destination_port', 'network_protocol',
fields = ('id', 'url', 'source', 'source_ip','source_ip_netmask', 'destination_ip', 'destination_ip_netmask','destination_port', 'network_protocol',
'transport_protocol', 'application_protocol')


Expand Down
91 changes: 46 additions & 45 deletions d3c/device_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,49 +16,45 @@
from django.contrib.contenttypes.models import ContentType


def get_current_value_for_device(device, findingField):
def get_current_value_for_device(device, findingfield):
"""
This function returns the corresponding Device value based on a DeviceFinding attribute.
"""
if not device:
return None

ff = findingField.lower()
if ff == 'manufacturer':
return str(device.device_type.manufacturer.name)
if ff == 'device_role':
return str(device.role)
if ff == 'device_type':
return str(device.device_type)
if ff == 'device_name':
return str(device.name)
if ff == 'device_family':
return str(device.device_type.custom_field_data.get('device_family', None))
if ff == 'description':
return str(device.device_type.description if device.device_type.description else None)
if ff == 'article_number':
return str(device.device_type.custom_field_data.get('article_number', None))
if ff == 'part_number':
return str(device.device_type.part_number if device.device_type.part_number else None)
if ff == 'serial_number':
return str(device.serial if device.serial else None)
if ff == 'status':
return str(device.status)
if ff == 'exposure':
return str(device.custom_field_data.get('exposure', None))
if ff == 'site':
return str(device.site)
if ff == 'rack':
return str(device.rack)
if ff == 'location':
return str(device.location)
if ff == 'is_safety_critical':
return str(device.custom_field_data.get('safety', None))
if ff == 'hardware_version':
return str(device.device_type.custom_field_data.get('hardware_version', None))
if ff == 'hardware_cpe':
return str(device.device_type.custom_field_data.get('cpe', None))
return findingField
ff = findingfield.lower()

# Mapping of field names to extraction functions
field_map = {
'manufacturer': lambda d: d.device_type.manufacturer.name,
'device_role': lambda d: d.role,
'device_type': lambda d: d.device_type,
'device_name': lambda d: d.name,
'device_family': lambda d: d.device_type.custom_field_data.get('device_family'),
'description': lambda d: d.device_type.description,
'article_number': lambda d: d.device_type.custom_field_data.get('article_number'),
'part_number': lambda d: d.device_type.part_number,
'serial_number': lambda d: d.serial,
'status': lambda d: d.status,
'exposure': lambda d: d.custom_field_data.get('exposure'),
'site': lambda d: d.site,
'rack': lambda d: d.rack,
'location': lambda d: d.location,
'is_safety_critical': lambda d: d.custom_field_data.get('safety'),
'hardware_version': lambda d: d.device_type.custom_field_data.get('hardware_version'),
'hardware_cpe': lambda d: d.device_type.custom_field_data.get('cpe'),
}
# Get the value using the field map
extractor = field_map.get(ff)
if extractor:
try:
value = extractor(device)
return str(value) if value is not None else None
except AttributeError:
return None

# Return original field name if not found
return findingfield


def create_and_assign_interface(device: Device, interface_name: str,
Expand All @@ -73,6 +69,7 @@ def create_and_assign_interface(device: Device, interface_name: str,

if ip_address:
ipaddr = IPAddress(address=ip_address, assigned_object=interface)
#### TODO
ipaddr.save()

if mac_address:
Expand Down Expand Up @@ -123,6 +120,7 @@ def change_device_type_keep_manufacturer(device, value):
"""
This function updates the device type of a device while keeping the manufacturer.
"""
### revision in branch P625
if device and device.device_type and value:

device_types = DeviceType.objects.filter(Q(model=value) | Q(slug=slugify(value)))
Expand Down Expand Up @@ -164,6 +162,7 @@ def change_device_role(device, value):
"""
This function updates the Device Role of a device.
"""
### Need revision for handling child parents properly. Issue #1
if device and value:
roles = DeviceRole.objects.filter(Q(name=value) | Q(slug=slugify(value)))
if roles.exists():
Expand Down Expand Up @@ -270,7 +269,7 @@ def change_device_exposure(device, value):
return True
else:
return False
except Exception as e:
except Exception:
return False


Expand All @@ -290,6 +289,7 @@ def change_device_router(device, mac, ip, value):
"""
This function assigns the is_router value to a device.
"""
### deprecated because roles can have child/parents #1
if device and value and (ip or mac):
interface = find_interface(device, mac, ip)
if interface:
Expand Down Expand Up @@ -350,6 +350,7 @@ def change_device_description(device, value):
"""
This function assigns a description to a device.
"""
# necessary since it is core field? check in data model revision
if device and value:
device.device_type.description = value
device.device_type.save()
Expand All @@ -361,6 +362,7 @@ def change_device_article_number(device, value):
"""
This function assigns an article number to a device.
"""
# Change in P625 into part_number
if device and value:
device.device_type.custom_field_data['article_number'] = value
device.device_type.save()
Expand Down Expand Up @@ -415,7 +417,7 @@ def find_interface(device, mac, ip):
This function performs a lookup based on the MAC and IP address
to check if the device has a corresponding interface.
"""

# TODO
parsed_ip = None

if ip:
Expand All @@ -436,11 +438,11 @@ def find_interface(device, mac, ip):
return None


def add_service(device, ip_address, network_protocol, transport_protocol, application_protocol, port):
def add_service(device, ip_address, ip_netmask, network_protocol, transport_protocol, application_protocol, port):
"""
This function creates a new Service object.
"""
result = False
# network protocol and result unused, check by data model revision

if not application_protocol or application_protocol == 'False':
application_protocol = 'Unspecified'
Expand All @@ -450,14 +452,13 @@ def add_service(device, ip_address, network_protocol, transport_protocol, applic
name=application_protocol,
protocol=transport_protocol,
ports=[int(port)])
ip = get_ip(ip_address)
ip = get_ip(ip_address, ip_netmask)
if ip and created:
ips = IPAddress.objects.filter(address=ip)
if ips.exists():
an_ip = IPAddress.objects.get(address=ip)
service.ipaddresses.add(an_ip)
service.save()

return True
except Exception as e:
return False
Expand All @@ -472,7 +473,7 @@ def add_software(device, name, firmware, version):
name = name if name else 'Unspecified'
version = version if version else 'Unspecified'
firmware = firmware == "True"

# break
software, created = Software.objects.get_or_create(name=name, is_firmware=firmware, version=version)

try:
Expand Down
4 changes: 2 additions & 2 deletions d3c/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Meta:
fields = ('id', 'device', 'source', 'confidence',
'description', 'device_role', 'serial_number', 'device_name', 'status', 'site', 'rack', 'location',
'device_type', 'serial_number', 'device_role', 'is_safety_critical',
'ip_address', 'mac_address', 'transport_protocol', 'application_protocol', 'port',
'ip_address', 'ip_netmask', 'mac_address', 'transport_protocol', 'application_protocol', 'port',
'is_router', 'manufacturer', 'oui', 'device_family', 'article_number', 'part_number',
'hardware_version', 'hardware_cpe', 'software_name', 'is_firmware', 'version',
'exposure', 'has_predicted_device', 'predicted_device')
Expand Down Expand Up @@ -53,7 +53,7 @@ class CommunicationFilterSet(NetBoxModelFilterSet):
"""
class Meta:
model = Communication
fields = ('id', 'source_device', 'destination_device', 'source_ip_addr', 'destination_ip_addr', 'destination_port',
fields = ('id', 'source_device', 'destination_device', 'source_ip_netmask','source_ip_addr','destination_ip_netmask' ,'destination_ip_addr', 'destination_port',
'network_protocol', 'transport_protocol', 'application_protocol')

def search(self, queryset, name, value):
Expand Down
Loading