mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-07 09:36:05 -08:00
Compare commits
7 Commits
d3326b3362
...
v25.11.29
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7125cea29b | ||
|
|
8586c5a307 | ||
|
|
0d81315809 | ||
|
|
8f193f1e2c | ||
|
|
b1eef8aa09 | ||
|
|
2da17f272c | ||
|
|
7bcb4586b2 |
@@ -44,8 +44,7 @@ Start NetAlertX in seconds with Docker:
|
||||
docker run -d \
|
||||
--network=host \
|
||||
--restart unless-stopped \
|
||||
-v /local_data_dir/config:/data/config \
|
||||
-v /local_data_dir/db:/data/db \
|
||||
-v /local_data_dir:/data \
|
||||
-v /etc/localtime:/etc/localtime:ro \
|
||||
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||
-e PORT=20211 \
|
||||
@@ -53,6 +52,8 @@ docker run -d \
|
||||
ghcr.io/jokob-sk/netalertx:latest
|
||||
```
|
||||
|
||||
Note: Your `/local_data_dir` should contain a `config` and `db` folder.
|
||||
|
||||
To deploy a containerized instance directly from the source repository, execute the following BASH sequence:
|
||||
```bash
|
||||
git clone https://github.com/jokob-sk/NetAlertX.git
|
||||
|
||||
@@ -16,8 +16,7 @@ Start the container via the **terminal** with a command similar to this one:
|
||||
docker run \
|
||||
--network=host \
|
||||
--restart unless-stopped \
|
||||
-v /local_data_dir/config:/data/config \
|
||||
-v /local_data_dir/db:/data/db \
|
||||
-v /local_data_dir:/data \
|
||||
-v /etc/localtime:/etc/localtime:ro \
|
||||
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||
-e PORT=20211 \
|
||||
@@ -26,6 +25,8 @@ docker run \
|
||||
|
||||
```
|
||||
|
||||
Note: Your `/local_data_dir` should contain a `config` and `db` folder.
|
||||
|
||||
> [!NOTE]
|
||||
> ⚠ The most important part is NOT to use the `-d` parameter so you see the error when the container crashes. Use this error in your issue description.
|
||||
|
||||
|
||||
@@ -125,9 +125,9 @@ docker compose up
|
||||
|
||||
### Modification 1: Use a Local Folder (Bind Mount)
|
||||
|
||||
By default, the baseline compose file uses a single named volume (netalertx_data) mounted at /data. This single-volume layout is preferred because NetAlertX manages both configuration and the database under /data (for example, /data/config and /data/db) via its web UI. Using one named volume simplifies permissions and portability: Docker manages the storage and NetAlertX manages the files inside /data.
|
||||
By default, the baseline compose file uses a single named volume (netalertx_data) mounted at `/data`. This single-volume layout is preferred because NetAlertX manages both configuration and the database under `/data` (for example, `/data/config` and `/data/db`) via its web UI. Using one named volume simplifies permissions and portability: Docker manages the storage and NetAlertX manages the files inside `/data`.
|
||||
|
||||
A two-volume layout that mounts /data/config and /data/db separately (for example, netalertx_config and netalertx_db) is supported for backward compatibility and some advanced workflows, but it is an abnormal/legacy layout and not recommended for new deployments.
|
||||
A two-volume layout that mounts `/data/config` and `/data/db` separately (for example, `netalertx_config` and `netalertx_db`) is supported for backward compatibility and some advanced workflows, but it is an abnormal/legacy layout and not recommended for new deployments.
|
||||
|
||||
However, if you prefer to have direct, file-level access to your configuration for manual editing, a "bind mount" is a simple alternative. This tells Docker to use a specific folder from your computer (the "host") inside the container.
|
||||
|
||||
|
||||
@@ -25,8 +25,7 @@ Head to [https://netalertx.com/](https://netalertx.com/) for more gifs and scree
|
||||
|
||||
```bash
|
||||
docker run -d --rm --network=host \
|
||||
-v /local_data_dir/config:/data/config \
|
||||
-v /local_data_dir/db:/data/db \
|
||||
-v /local_data_dir:/data \
|
||||
-v /etc/localtime:/etc/localtime \
|
||||
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||
-e PORT=20211 \
|
||||
|
||||
@@ -78,7 +78,7 @@ In the **Environment variables** section of Portainer, add the following:
|
||||
>
|
||||
> `sudo chown -R 20211:20211 /local_data_dir`
|
||||
>
|
||||
> `sudo chmod -R a+rwx /local_data_dir1`
|
||||
> `sudo chmod -R a+rwx /local_data_dir`
|
||||
>
|
||||
|
||||
|
||||
|
||||
@@ -46,8 +46,7 @@ NetAlertX requires certain paths to be writable at runtime. These paths should b
|
||||
|
||||
```bash
|
||||
docker run -it --rm --name netalertx --user "0" \
|
||||
-v /local_data_dir/config:/data/config \
|
||||
-v /local_data_dir/db:/data/db \
|
||||
-v /local_data_dir:/data \
|
||||
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||
ghcr.io/jokob-sk/netalertx:latest
|
||||
```
|
||||
@@ -84,8 +83,7 @@ services:
|
||||
- NET_BIND_SERVICE # Required to bind to privileged ports (nbtscan)
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /local_data_dir/config:/data/config
|
||||
- /local_data_dir/db:/data/db
|
||||
- /local_data_dir:/data
|
||||
- /etc/localtime:/etc/localtime
|
||||
environment:
|
||||
- PORT=20211
|
||||
|
||||
@@ -284,8 +284,7 @@ services:
|
||||
- NET_BIND_SERVICE # 🆕 New line
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /local_data_dir/config:/data/config # 🆕 This has changed from /app to /data
|
||||
- /local_data_dir/db:/data/db # 🆕 This has changed from /app to /data
|
||||
- /local_data_dir:/data # 🆕 This folder contains your /db and /config directories and the parent changed from /app to /data
|
||||
# Ensuring the timezone is the same as on the server - make sure also the TIMEZONE setting is configured
|
||||
- /etc/localtime:/etc/localtime:ro # 🆕 New line
|
||||
environment:
|
||||
|
||||
@@ -47,8 +47,7 @@ services:
|
||||
- NET_ADMIN
|
||||
- NET_BIND_SERVICE
|
||||
volumes:
|
||||
- /app_storage/netalertx/config:/data/config
|
||||
- /app_storage/netalertx/db:/data/db
|
||||
- /app_storage/netalertx:/data
|
||||
# to sync with system time
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
tmpfs:
|
||||
@@ -66,10 +65,7 @@ services:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- /volume1/app_storage/netalertx/config:/data/config
|
||||
- /volume1/app_storage/netalertx/db:/data/db
|
||||
# (optional) useful for debugging if you have issues setting up the container
|
||||
# - local/path/logs:/tmp/log <- commented out with # ⚠
|
||||
- /volume1/app_storage/netalertx:/data
|
||||
```
|
||||
|
||||

|
||||
|
||||
@@ -72,7 +72,7 @@ a[target="_blank"] {
|
||||
|
||||
[data-is-valid="0"] {
|
||||
/* border: 1px solid red; */
|
||||
background-color: #ff4b4b;
|
||||
background-color: #ff4b4b !important;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
<!-- page script ----------------------------------------------------------- -->
|
||||
<script>
|
||||
var deviceStatus = 'all';
|
||||
var tableRows = getCache ("nax_parTableRows") == "" ? parseInt(getSetting("UI_DEFAULT_PAGE_SIZE")) : getCache ("nax_parTableRows") ;
|
||||
|
||||
var tableOrder = getCache ("nax_parTableOrder") == "" ? [[3,'desc'], [0,'asc']] : JSON.parse(getCache ("nax_parTableOrder")) ;
|
||||
|
||||
var tableColumnHide = [];
|
||||
@@ -563,6 +563,9 @@ function initializeDatatable (status) {
|
||||
status = 'my_devices'
|
||||
}
|
||||
|
||||
// retrieve page size
|
||||
var tableRows = getCache ("nax_parTableRows") == "" ? parseInt(getSetting("UI_DEFAULT_PAGE_SIZE")) : getCache ("nax_parTableRows") ;
|
||||
|
||||
// Save status selected
|
||||
deviceStatus = status;
|
||||
|
||||
|
||||
@@ -1622,7 +1622,6 @@ async function executeOnce() {
|
||||
|
||||
if (!isAppInitialized()) {
|
||||
try {
|
||||
console.log("HERE");
|
||||
|
||||
await waitForGraphQLServer(); // Wait for the server to start
|
||||
|
||||
|
||||
2
front/php/templates/language/it_it.json
Executable file → Normal file
2
front/php/templates/language/it_it.json
Executable file → Normal file
@@ -311,7 +311,7 @@
|
||||
"Gen_Filter": "Filtro",
|
||||
"Gen_Generate": "Genera",
|
||||
"Gen_InvalidMac": "Indirizzo Mac non valido.",
|
||||
"Gen_Invalid_Value": "",
|
||||
"Gen_Invalid_Value": "È stato inserito un valore non valido",
|
||||
"Gen_LockedDB": "ERRORE: il DB potrebbe essere bloccato, controlla F12 Strumenti di sviluppo -> Console o riprova più tardi.",
|
||||
"Gen_NetworkMask": "Maschera di rete",
|
||||
"Gen_Offline": "Offline",
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import hashlib
|
||||
import re
|
||||
import nmap
|
||||
|
||||
@@ -17,6 +16,7 @@ from plugin_helper import Plugin_Objects # noqa: E402 [flake8 lint suppression]
|
||||
from logger import mylog, Logger # noqa: E402 [flake8 lint suppression]
|
||||
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
|
||||
from const import logPath # noqa: E402 [flake8 lint suppression]
|
||||
from utils.crypto_utils import string_to_mac_hash # noqa: E402 [flake8 lint suppression]
|
||||
import conf # noqa: E402 [flake8 lint suppression]
|
||||
from pytz import timezone # noqa: E402 [flake8 lint suppression]
|
||||
|
||||
@@ -177,16 +177,6 @@ def parse_nmap_xml(xml_output, interface, fakeMac):
|
||||
return devices_list
|
||||
|
||||
|
||||
def string_to_mac_hash(input_string):
|
||||
# Calculate a hash using SHA-256
|
||||
sha256_hash = hashlib.sha256(input_string.encode()).hexdigest()
|
||||
|
||||
# Take the first 12 characters of the hash and format as a MAC address
|
||||
mac_hash = ':'.join(sha256_hash[i:i + 2] for i in range(0, 12, 2))
|
||||
|
||||
return mac_hash
|
||||
|
||||
|
||||
# ===============================================================================
|
||||
# BEGIN
|
||||
# ===============================================================================
|
||||
|
||||
@@ -13,9 +13,6 @@ The plugin connects to your Pi-hole’s API and retrieves:
|
||||
|
||||
NetAlertX then uses this information to match or create devices in your system.
|
||||
|
||||
> [!TIP]
|
||||
> Some tip.
|
||||
|
||||
### Quick setup guide
|
||||
|
||||
* You are running **Pi-hole v6** or newer.
|
||||
@@ -35,16 +32,8 @@ No additional Pi-hole configuration is required.
|
||||
| **PIHOLEAPI_SSL_VERIFY** | Whether to verify HTTPS certificates. Disable only for self-signed certificates. |
|
||||
| **PIHOLEAPI_RUN_TIMEOUT** | Request timeout in seconds. |
|
||||
| **PIHOLEAPI_API_MAXCLIENTS** | Maximum number of devices to request from Pi-hole. Defaults are usually fine. |
|
||||
| **PIHOLEAPI_FAKE_MAC** | Generate FAKE MAC from IP. |
|
||||
|
||||
### Example Configuration
|
||||
|
||||
| Setting Key | Sample Value |
|
||||
| ---------------------------- | -------------------------------------------------- |
|
||||
| **PIHOLEAPI_URL** | `http://pi.hole/` |
|
||||
| **PIHOLEAPI_PASSWORD** | `passw0rd` |
|
||||
| **PIHOLEAPI_SSL_VERIFY** | `true` |
|
||||
| **PIHOLEAPI_RUN_TIMEOUT** | `30` |
|
||||
| **PIHOLEAPI_API_MAXCLIENTS** | `500` |
|
||||
|
||||
### ⚠️ Troubleshooting
|
||||
|
||||
@@ -110,6 +99,32 @@ Then re-run the plugin.
|
||||
|
||||
---
|
||||
|
||||
#### ❌ Some devices are missing
|
||||
|
||||
Check:
|
||||
|
||||
* Pi-hole shows devices under **Settings → Network**
|
||||
* NetAlertX logs contain:
|
||||
|
||||
```
|
||||
[PIHOLEAPI] Skipping invalid MAC (see PIHOLEAPI_FAKE_MAC setting) ...
|
||||
```
|
||||
|
||||
If devices are missing:
|
||||
|
||||
* The app skipps devices with invalid MACs
|
||||
* Enable PIHOLEAPI_FAKE_MAC if you want to import these devices with a fake mac and you are not concerned with data inconsistencies later on
|
||||
|
||||
Try enabling PIHOLEAPI_FAKE_MAC:
|
||||
|
||||
```
|
||||
PIHOLEAPI_FAKE_MAC = 1
|
||||
```
|
||||
|
||||
Then re-run the plugin.
|
||||
|
||||
---
|
||||
|
||||
#### ❌ Wrong or missing hostnames
|
||||
|
||||
Pi-hole only reports names it knows from:
|
||||
|
||||
@@ -279,6 +279,41 @@
|
||||
"string": "Maximum time in seconds to wait for the script to finish. If this time is exceeded the script is aborted."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "FAKE_MAC",
|
||||
"type": {
|
||||
"dataType": "boolean",
|
||||
"elements": [
|
||||
{
|
||||
"elementType": "input",
|
||||
"elementOptions": [
|
||||
{
|
||||
"type": "checkbox"
|
||||
}
|
||||
],
|
||||
"transformers": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"default_value": false,
|
||||
"options": [],
|
||||
"localized": [
|
||||
"name",
|
||||
"description"
|
||||
],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Fake MAC if empty"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Some PiHole devices don't have a MAC assigned. Enabling the FAKE_MAC setting generates a fake MAC address from the IP address to track devices, but it may cause inconsistencies if IPs change or devices are re-discovered with a different MAC. Static IPs are recommended. Device type and icon might not be detected correctly and some plugins might fail if they depend on a valid MAC address. When unchecked, devices with empty MAC addresses are skipped."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"database_column_definitions": [
|
||||
|
||||
@@ -23,6 +23,7 @@ from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
|
||||
from const import logPath # noqa: E402 [flake8 lint suppression]
|
||||
import conf # noqa: E402 [flake8 lint suppression]
|
||||
from pytz import timezone # noqa: E402 [flake8 lint suppression]
|
||||
from utils.crypto_utils import string_to_mac_hash # noqa: E402 [flake8 lint suppression]
|
||||
|
||||
# Setup timezone & logger using standard NAX helpers
|
||||
conf.tz = timezone(get_setting_value('TIMEZONE'))
|
||||
@@ -42,6 +43,7 @@ PIHOLEAPI_SES_CSRF = None
|
||||
PIHOLEAPI_API_MAXCLIENTS = None
|
||||
PIHOLEAPI_VERIFY_SSL = True
|
||||
PIHOLEAPI_RUN_TIMEOUT = 10
|
||||
PIHOLEAPI_FAKE_MAC = get_setting_value('PIHOLEAPI_FAKE_MAC')
|
||||
VERSION_DATE = "NAX-PIHOLEAPI-1.0"
|
||||
|
||||
|
||||
@@ -222,8 +224,14 @@ def gather_device_entries():
|
||||
if ip in iplist:
|
||||
lastQuery = str(now_ts)
|
||||
|
||||
tmpMac = hwaddr.lower()
|
||||
|
||||
# ensure fake mac if enabled
|
||||
if PIHOLEAPI_FAKE_MAC and is_mac(tmpMac) is False:
|
||||
tmpMac = string_to_mac_hash(ip)
|
||||
|
||||
entries.append({
|
||||
'mac': hwaddr.lower(),
|
||||
'mac': tmpMac,
|
||||
'ip': ip,
|
||||
'name': name,
|
||||
'macVendor': macVendor,
|
||||
@@ -281,7 +289,7 @@ def main():
|
||||
foreignKey=str(entry['mac'])
|
||||
)
|
||||
else:
|
||||
mylog('verbose', [f"[{pluginName}] Skipping invalid MAC: {entry['name']}|{entry['mac']}|{entry['ip']}"])
|
||||
mylog('verbose', [f"[{pluginName}] Skipping invalid MAC (see PIHOLEAPI_FAKE_MAC setting): {entry['name']}|{entry['mac']}|{entry['ip']}"])
|
||||
|
||||
# Write result file for NetAlertX to ingest
|
||||
plugin_objects.write_result_file()
|
||||
|
||||
@@ -70,3 +70,13 @@ def generate_deterministic_guid(plugin, primary_id, secondary_id):
|
||||
"""Generates a deterministic GUID based on plugin, primary ID, and secondary ID."""
|
||||
data = f"{plugin}-{primary_id}-{secondary_id}".encode("utf-8")
|
||||
return str(uuid.UUID(hashlib.md5(data).hexdigest()))
|
||||
|
||||
|
||||
def string_to_mac_hash(input_string):
|
||||
# Calculate a hash using SHA-256
|
||||
sha256_hash = hashlib.sha256(input_string.encode()).hexdigest()
|
||||
|
||||
# Take the first 12 characters of the hash and format as a MAC address
|
||||
mac_hash = ':'.join(sha256_hash[i:i + 2] for i in range(0, 12, 2))
|
||||
|
||||
return mac_hash
|
||||
|
||||
Reference in New Issue
Block a user