NetScan¶
-Network Scanner to analyze devices connecting to the router and alert accordingly.
-This app can display intruders’ IP addresses, MAC addresses, and lets the user Ping the device, and even Block the device.
-Block IP Address feature helps the user to remove the specified connections and block the specific IP address.
+NetSec (Network Security)¶
+NetSec is a tool to analyze devices connecting to the router and alert accordingly when a new device is connected.
+This app can display and store intruders’ IP address, MAC address, and Block the device.
-Blocking device feature is currently available only for
Netgear
router users.
+from netsec import network_monitor, SupportedModules
+
+if __name__ == '__main__':
+ network_monitor(module=SupportedModules.att, init=True) # Create snapshot
+ network_monitor(module=SupportedModules.att, init=False) # Run the scan
@@ -61,7 +64,7 @@ Coding Standardsisort
-Release Notes¶
+Release Notes¶
Requirement
python -m pip install changelog-generator
@@ -86,7 +89,7 @@ Linting
Runbook¶
-https://thevickypedia.github.io/netscan/
+https://thevickypedia.github.io/NetSec/
@@ -100,7 +103,7 @@ RunbookTable of Contents
-- NetScan
+- NetSec (Network Security)
- Coding Standards
- Release Notes
- Linting
@@ -113,7 +116,7 @@ Table of Contents
Previous topic
+ title="previous chapter">Welcome to NetSec’s documentation!
@@ -58,22 +58,15 @@ Index
A
@@ -81,13 +74,13 @@ A
B
@@ -95,31 +88,27 @@ B
C
+
-
D
@@ -127,7 +116,7 @@ D
F
@@ -135,17 +124,17 @@ F
G
G
L
@@ -165,69 +154,74 @@ M
module
+
+
+
+N
+
+
+ - netgear (netsec.modules.models.SupportedModules attribute)
+
-
- modules.att
+ netsec.analyzer
-
-
+
-
+ -
+ netsec.modules.settings
-
N
-
-
-
+ - network_monitor() (in module netsec.analyzer)
- - notify() (in module modules.helper)
+
- notify() (in module netsec.modules.helper)
@@ -235,7 +229,7 @@ N
P
@@ -243,16 +237,16 @@ P
R
@@ -261,11 +255,11 @@ R
S
@@ -301,7 +295,7 @@ Navigation
-
modules |
-
+
diff --git a/docs/index.html b/docs/index.html
index b43bb34..04f0d10 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -6,7 +6,7 @@
- Welcome to NetScan’s documentation! — NetScan documentation
+ Welcome to NetSec’s documentation! — NetSec documentation
@@ -18,7 +18,7 @@
-
+
@@ -42,12 +42,12 @@ Navigation
-
-Welcome to NetScan’s documentation!¶
+
+Welcome to NetSec’s documentation!¶
-- NetScan
+- NetSec (Network Security)
- Coding Standards
- Release Notes
- Linting
@@ -57,11 +57,11 @@ Welcome to NetScan’s documentation!
-NetScan¶
+
+NetSec¶
--
-analyzer.network_monitor(module: SupportedModules, init: bool = True) NoReturn ¶
+-
+netsec.analyzer.network_monitor(module: SupportedModules, init: bool = True) NoReturn ¶
Monitor devices connected to the network.
- Parameters:
@@ -74,11 +74,11 @@ Welcome to NetScan’s documentation!
-At&t¶
+
+At&t¶
--
-class modules.att.Device(dictionary: dict)¶
+-
+class netsec.modules.att.Device(dictionary: dict)¶
Convert dictionary into a device object.
>>> Device
@@ -86,20 +86,20 @@ Welcome to NetScan’s documentation!
--
-modules.att.create_snapshot() NoReturn ¶
+-
+netsec.modules.att.create_snapshot() NoReturn ¶
Creates a snapshot.json which is used to determine the known and unknown devices.
--
-modules.att.format_key(key: str) str ¶
+-
+netsec.modules.att.format_key(key: str) str ¶
Format the key to match the Device object.
--
-modules.att.generate_dataframe() DataFrame ¶
+-
+netsec.modules.att.generate_dataframe() DataFrame ¶
Generate a dataframe using the devices information from router web page.
- Returns:
@@ -112,8 +112,8 @@ Welcome to NetScan’s documentation!
--
-modules.att.get_attached_devices() Generator[Device] ¶
+-
+netsec.modules.att.get_attached_devices() Generator[Device] ¶
Get all devices connected to the router.
- Yields:
@@ -123,30 +123,30 @@ Welcome to NetScan’s documentation!
--
-modules.att.get_ipaddress() str ¶
+-
+netsec.modules.att.get_ipaddress() str ¶
Get network id from the current IP address.
--
-modules.att.run() NoReturn ¶
+-
+netsec.modules.att.run() NoReturn ¶
Trigger to initiate a Network Scan and block the devices that are not present in snapshot.json
file.
-
-Netgear¶
+
+Netgear¶
--
-class modules.netgear.LocalIPScan¶
+-
+class netsec.modules.netgear.LocalIPScan¶
Connector to scan devices in the same IP range using Netgear API
.
>>> LocalIPScan
--
-allow(device: Union[str, Device]) Optional[Device] ¶
+-
+allow(device: Union[str, Device]) Optional[Device] ¶
Allows internet access to a device.
- Parameters:
@@ -156,14 +156,14 @@ Welcome to NetScan’s documentation!Returns the device object received from get_device_by_name()
method.
- Return type:
--
+
-
--
-always_allow(device: Device) NoReturn ¶
+-
+always_allow(device: Device) NoReturn ¶
Allows internet access to a device.
Saves the device name to snapshot.json
to not block in the future.
Removes the device name from blocked.json
if an entry is present.
@@ -175,8 +175,8 @@ Welcome to NetScan’s documentation!
--
-block(device: Union[str, Device]) Optional[Device] ¶
+-
+block(device: Union[str, Device]) Optional[Device] ¶
Blocks internet access to a device.
- Parameters:
@@ -186,138 +186,114 @@ Welcome to NetScan’s documentation!Returns the device object received from get_device_by_name()
method.
- Return type:
--
+
-
-
-Helper¶
+
+Helper¶
--
-modules.helper.custom_time(*args: Formatter) struct_time ¶
-Creates custom timezone for logging
which gets used only when invoked by Docker
.
-This is used only when triggered within a docker container
as it uses UTC timezone.
-
-- Parameters:
-*args – Takes Formatter
object and current epoch time as arguments passed by formatTime
from logging
.
-
-- Returns:
-A struct_time object which is a tuple of:
-current year, month, day, hour, minute, second, weekday, year day and dst (Daylight Saving Time)
-
-- Return type:
-struct_time
-
-
-
-
-
-
-Models¶
+
+Models¶
-
-Settings¶
+
+Settings¶
--
-class modules.settings.Config¶
+-
+class netsec.modules.settings.Config¶
Wrapper for all the environment variables.
--
-blocked = 'fileio/blocked.yaml'¶
-
-
-
@@ -342,13 +318,13 @@ Indices and tablesTable of Contents
-- Welcome to NetScan’s documentation!
-- NetScan
-- At&t
-- Netgear
-- Helper
-- Models
-- Settings
+- Welcome to NetSec’s documentation!
+- NetSec
+- At&t
+- Netgear
+- Helper
+- Models
+- Settings
- Indices and tables
@@ -356,7 +332,7 @@ Table of Contents
Next topic
+ title="next chapter">NetSec (Network Security)
@@ -44,52 +44,48 @@ Navigation
Python Module Index
-
- a
-
-
-
- analyzer
-
-
-
- m
+
+ n
- modules
+ netsec
+
+
+
+
+ netsec.analyzer
- modules.att
+ netsec.modules.att
- modules.helper
+ netsec.modules.helper
- modules.models
+ netsec.modules.models
- modules.netgear
+ netsec.modules.netgear
- modules.settings
+ netsec.modules.settings
@@ -123,7 +119,7 @@ Navigation
-
modules |
-
+
diff --git a/docs/search.html b/docs/search.html
index 733416b..b4ecf7f 100644
--- a/docs/search.html
+++ b/docs/search.html
@@ -5,7 +5,7 @@
- Search — NetScan documentation
+ Search — NetSec documentation
@@ -33,7 +33,7 @@ Navigation
-
modules |
-
+
@@ -93,7 +93,7 @@
Navigation
-
modules |
-
+
diff --git a/docs/searchindex.js b/docs/searchindex.js
index 5bdcde1..fe4b9d5 100644
--- a/docs/searchindex.js
+++ b/docs/searchindex.js
@@ -1 +1 @@
-Search.setIndex({"docnames": ["README", "index"], "filenames": ["README.md", "index.rst"], "titles": ["NetScan", "Welcome to NetScan\u2019s documentation!"], "terms": {"network": [0, 1], "scanner": 0, "analyz": [0, 1], "devic": [0, 1], "connect": [0, 1], "router": [0, 1], "alert": 0, "accordingli": 0, "thi": [0, 1], "app": 0, "can": 0, "displai": 0, "intrud": 0, "ip": [0, 1], "address": [0, 1], "mac": 0, "let": 0, "user": 0, "ping": 0, "even": 0, "block": [0, 1], "featur": 0, "help": 0, "remov": [0, 1], "specifi": 0, "specif": 0, "i": [0, 1], "current": [0, 1], "avail": 0, "onli": [0, 1], "netgear": 0, "docstr": 0, "format": [0, 1], "googl": 0, "style": 0, "convent": 0, "pep": 0, "8": 0, "clean": 0, "pre": 0, "commit": 0, "hook": 0, "flake8": 0, "isort": 0, "requir": 0, "python": 0, "m": 0, "pip": 0, "instal": 0, "changelog": 0, "gener": [0, 1], "usag": 0, "revers": 0, "f": 0, "release_not": 0, "rst": 0, "t": 0, "precommit": 0, "ensur": 0, "doc": 0, "creation": 0, "ar": [0, 1], "run": [0, 1], "everi": 0, "sphinx": 0, "5": 0, "1": 0, "recommonmark": 0, "all": [0, 1], "file": [0, 1], "http": 0, "thevickypedia": 0, "github": 0, "io": 0, "code": 1, "standard": 1, "releas": 1, "note": 1, "lint": 1, "runbook": 1, "network_monitor": 1, "modul": 1, "supportedmodul": 1, "init": 1, "bool": 1, "true": 1, "noreturn": 1, "monitor": 1, "paramet": 1, "scan": 1, "support": 1, "ani": 1, "take": 1, "boolean": 1, "valu": 1, "creat": 1, "snapshot": 1, "actual": 1, "class": 1, "att": 1, "dictionari": 1, "dict": 1, "convert": 1, "object": 1, "create_snapshot": 1, "json": 1, "which": 1, "us": 1, "determin": 1, "known": 1, "unknown": 1, "format_kei": 1, "kei": 1, "str": 1, "match": 1, "generate_datafram": 1, "datafram": 1, "inform": 1, "from": 1, "web": 1, "page": 1, "return": 1, "list": 1, "data": 1, "frame": 1, "type": 1, "get_attached_devic": 1, "get": 1, "yield": 1, "each": 1, "get_ipaddress": 1, "id": 1, "trigger": 1, "initi": 1, "present": 1, "localipscan": 1, "connector": 1, "same": 1, "rang": 1, "api": 1, "allow": 1, "union": 1, "option": 1, "internet": 1, "access": 1, "name": 1, "an": 1, "argument": 1, "receiv": 1, "get_device_by_nam": 1, "method": 1, "always_allow": 1, "save": 1, "futur": 1, "entri": 1, "fals": 1, "custom_tim": 1, "arg": 1, "formatt": 1, "struct_tim": 1, "custom": 1, "timezon": 1, "log": 1, "when": 1, "invok": 1, "docker": 1, "within": 1, "contain": 1, "utc": 1, "epoch": 1, "time": 1, "pass": 1, "formattim": 1, "A": 1, "tupl": 1, "year": 1, "month": 1, "dai": 1, "hour": 1, "minut": 1, "second": 1, "weekdai": 1, "dst": 1, "daylight": 1, "notifi": 1, "msg": 1, "send": 1, "email": 1, "notif": 1, "threat": 1, "messag": 1, "ha": 1, "sent": 1, "devicestatu": 1, "statu": 1, "string": 1, "config": 1, "wrapper": 1, "environ": 1, "variabl": 1, "fileio": 1, "yaml": 1, "none": 1, "gmail_pass": 1, "gmail_us": 1, "phone": 1, "recipi": 1, "router_pass": 1, "index": 1, "search": 1}, "objects": {"": [[1, 0, 0, "-", "analyzer"]], "analyzer": [[1, 1, 1, "", "network_monitor"]], "modules": [[1, 0, 0, "-", "att"], [1, 0, 0, "-", "helper"], [1, 0, 0, "-", "models"], [1, 0, 0, "-", "netgear"], [1, 0, 0, "-", "settings"]], "modules.att": [[1, 2, 1, "", "Device"], [1, 1, 1, "", "create_snapshot"], [1, 1, 1, "", "format_key"], [1, 1, 1, "", "generate_dataframe"], [1, 1, 1, "", "get_attached_devices"], [1, 1, 1, "", "get_ipaddress"], [1, 1, 1, "", "run"]], "modules.helper": [[1, 1, 1, "", "custom_time"], [1, 1, 1, "", "notify"]], "modules.models": [[1, 2, 1, "", "DeviceStatus"], [1, 2, 1, "", "SupportedModules"]], "modules.models.DeviceStatus": [[1, 3, 1, "", "allow"], [1, 3, 1, "", "block"]], "modules.models.SupportedModules": [[1, 3, 1, "", "att"], [1, 3, 1, "", "netgear"]], "modules.netgear": [[1, 2, 1, "", "LocalIPScan"]], "modules.netgear.LocalIPScan": [[1, 4, 1, "", "allow"], [1, 4, 1, "", "always_allow"], [1, 4, 1, "", "block"], [1, 4, 1, "", "create_snapshot"], [1, 4, 1, "", "run"]], "modules.settings": [[1, 2, 1, "", "Config"]], "modules.settings.Config": [[1, 3, 1, "", "blocked"], [1, 3, 1, "", "docker"], [1, 3, 1, "", "gmail_pass"], [1, 3, 1, "", "gmail_user"], [1, 3, 1, "", "phone"], [1, 3, 1, "", "recipient"], [1, 3, 1, "", "router_pass"], [1, 3, 1, "", "snapshot"]]}, "objtypes": {"0": "py:module", "1": "py:function", "2": "py:class", "3": "py:attribute", "4": "py:method"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "function", "Python function"], "2": ["py", "class", "Python class"], "3": ["py", "attribute", "Python attribute"], "4": ["py", "method", "Python method"]}, "titleterms": {"netscan": [0, 1], "code": 0, "standard": 0, "releas": 0, "note": 0, "lint": 0, "runbook": 0, "welcom": 1, "": 1, "document": 1, "read": 1, "me": 1, "At": 1, "t": 1, "netgear": 1, "helper": 1, "model": 1, "set": 1, "indic": 1, "tabl": 1}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 6, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx": 56}})
\ No newline at end of file
+Search.setIndex({"docnames": ["README", "index"], "filenames": ["README.md", "index.rst"], "titles": ["NetSec (Network Security)", "Welcome to NetSec\u2019s documentation!"], "terms": {"i": [0, 1], "tool": 0, "analyz": [0, 1], "devic": [0, 1], "connect": [0, 1], "router": [0, 1], "alert": 0, "accordingli": 0, "when": [0, 1], "new": 0, "thi": 0, "app": 0, "can": 0, "displai": 0, "store": 0, "intrud": 0, "ip": [0, 1], "address": [0, 1], "mac": 0, "block": [0, 1], "featur": 0, "current": [0, 1], "avail": 0, "onli": 0, "netgear": 0, "user": 0, "from": [0, 1], "import": 0, "network_monitor": [0, 1], "supportedmodul": [0, 1], "__name__": 0, "__main__": 0, "modul": [0, 1], "att": [0, 1], "init": [0, 1], "true": [0, 1], "creat": [0, 1], "snapshot": [0, 1], "fals": [0, 1], "run": [0, 1], "scan": [0, 1], "docstr": 0, "format": [0, 1], "googl": 0, "style": 0, "convent": 0, "pep": 0, "8": 0, "clean": 0, "pre": 0, "commit": 0, "hook": 0, "flake8": 0, "isort": 0, "requir": 0, "python": 0, "m": 0, "pip": 0, "instal": 0, "changelog": 0, "gener": [0, 1], "usag": 0, "revers": 0, "f": 0, "release_not": 0, "rst": 0, "t": 0, "precommit": 0, "ensur": 0, "doc": 0, "creation": 0, "ar": [0, 1], "everi": 0, "sphinx": 0, "5": 0, "1": 0, "recommonmark": 0, "all": [0, 1], "file": [0, 1], "http": 0, "thevickypedia": 0, "github": 0, "io": 0, "network": 1, "secur": 1, "code": 1, "standard": 1, "releas": 1, "note": 1, "lint": 1, "runbook": 1, "bool": 1, "noreturn": 1, "monitor": 1, "paramet": 1, "support": 1, "ani": 1, "take": 1, "boolean": 1, "valu": 1, "actual": 1, "class": 1, "dictionari": 1, "dict": 1, "convert": 1, "object": 1, "create_snapshot": 1, "json": 1, "which": 1, "us": 1, "determin": 1, "known": 1, "unknown": 1, "format_kei": 1, "kei": 1, "str": 1, "match": 1, "generate_datafram": 1, "datafram": 1, "inform": 1, "web": 1, "page": 1, "return": 1, "list": 1, "data": 1, "frame": 1, "type": 1, "get_attached_devic": 1, "get": 1, "yield": 1, "each": 1, "get_ipaddress": 1, "id": 1, "trigger": 1, "initi": 1, "present": 1, "localipscan": 1, "connector": 1, "same": 1, "rang": 1, "api": 1, "allow": 1, "union": 1, "option": 1, "internet": 1, "access": 1, "name": 1, "an": 1, "argument": 1, "receiv": 1, "get_device_by_nam": 1, "method": 1, "always_allow": 1, "save": 1, "futur": 1, "remov": 1, "entri": 1, "notifi": 1, "msg_dict": 1, "send": 1, "email": 1, "notif": 1, "threat": 1, "messag": 1, "sent": 1, "templat": 1, "devicestatu": 1, "statu": 1, "string": 1, "config": 1, "wrapper": 1, "environ": 1, "variabl": 1, "pathlik": 1, "fileio": 1, "yaml": 1, "gmail_pass": 1, "anystr": 1, "none": 1, "gmail_us": 1, "phone": 1, "recipi": 1, "router_pass": 1, "index": 1, "search": 1}, "objects": {"netsec": [[1, 0, 0, "-", "analyzer"]], "netsec.analyzer": [[1, 1, 1, "", "network_monitor"]], "netsec.modules": [[1, 0, 0, "-", "att"], [1, 0, 0, "-", "helper"], [1, 0, 0, "-", "models"], [1, 0, 0, "-", "netgear"], [1, 0, 0, "-", "settings"]], "netsec.modules.att": [[1, 2, 1, "", "Device"], [1, 1, 1, "", "create_snapshot"], [1, 1, 1, "", "format_key"], [1, 1, 1, "", "generate_dataframe"], [1, 1, 1, "", "get_attached_devices"], [1, 1, 1, "", "get_ipaddress"], [1, 1, 1, "", "run"]], "netsec.modules.helper": [[1, 1, 1, "", "notify"]], "netsec.modules.models": [[1, 2, 1, "", "DeviceStatus"], [1, 2, 1, "", "SupportedModules"]], "netsec.modules.models.DeviceStatus": [[1, 3, 1, "", "allow"], [1, 3, 1, "", "block"]], "netsec.modules.models.SupportedModules": [[1, 3, 1, "", "att"], [1, 3, 1, "", "netgear"]], "netsec.modules.netgear": [[1, 2, 1, "", "LocalIPScan"]], "netsec.modules.netgear.LocalIPScan": [[1, 4, 1, "", "allow"], [1, 4, 1, "", "always_allow"], [1, 4, 1, "", "block"], [1, 4, 1, "", "create_snapshot"], [1, 4, 1, "", "run"]], "netsec.modules.settings": [[1, 2, 1, "", "Config"]], "netsec.modules.settings.Config": [[1, 3, 1, "", "blocked"], [1, 3, 1, "", "gmail_pass"], [1, 3, 1, "", "gmail_user"], [1, 3, 1, "", "phone"], [1, 3, 1, "", "recipient"], [1, 3, 1, "", "router_pass"], [1, 3, 1, "", "snapshot"]]}, "objtypes": {"0": "py:module", "1": "py:function", "2": "py:class", "3": "py:attribute", "4": "py:method"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "function", "Python function"], "2": ["py", "class", "Python class"], "3": ["py", "attribute", "Python attribute"], "4": ["py", "method", "Python method"]}, "titleterms": {"netsec": [0, 1], "network": 0, "secur": 0, "code": 0, "standard": 0, "releas": 0, "note": 0, "lint": 0, "runbook": 0, "welcom": 1, "": 1, "document": 1, "read": 1, "me": 1, "At": 1, "t": 1, "netgear": 1, "helper": 1, "model": 1, "set": 1, "indic": 1, "tabl": 1}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 6, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx": 56}})
\ No newline at end of file
diff --git a/docs_gen/conf.py b/docs_gen/conf.py
index ac22948..8a854a6 100644
--- a/docs_gen/conf.py
+++ b/docs_gen/conf.py
@@ -18,7 +18,7 @@
# -- Project information -----------------------------------------------------
-project = 'NetScan'
+project = 'NetSec'
copyright = '2021, Vignesh Sivanandha Rao'
author = 'Vignesh Sivanandha Rao'
diff --git a/docs_gen/index.rst b/docs_gen/index.rst
index d788cb7..fbe96c7 100644
--- a/docs_gen/index.rst
+++ b/docs_gen/index.rst
@@ -1,10 +1,10 @@
-.. NetScan documentation master file, created by
+.. NetSec documentation master file, created by
sphinx-quickstart on Thu Aug 26 16:15:06 2021.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
-Welcome to NetScan's documentation!
-===================================
+Welcome to NetSec's documentation!
+==================================
.. toctree::
:maxdepth: 2
@@ -12,45 +12,45 @@ Welcome to NetScan's documentation!
README
-NetScan
-=======
+NetSec
+======
-.. automodule:: analyzer
+.. automodule:: netsec.analyzer
:members:
:undoc-members:
At&t
====
-.. automodule:: modules.att
+.. automodule:: netsec.modules.att
:members:
:undoc-members:
Netgear
=======
-.. automodule:: modules.netgear
+.. automodule:: netsec.modules.netgear
:members:
:undoc-members:
Helper
======
-.. automodule:: modules.helper
+.. automodule:: netsec.modules.helper
:members:
:undoc-members:
Models
======
-.. automodule:: modules.models
+.. automodule:: netsec.modules.models
:members:
:undoc-members:
Settings
========
-.. automodule:: modules.settings
+.. automodule:: netsec.modules.settings
:members:
:undoc-members:
diff --git a/modules/helper.py b/modules/helper.py
deleted file mode 100644
index 52331ee..0000000
--- a/modules/helper.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import logging
-import time
-from datetime import datetime, timezone
-from typing import NoReturn
-
-import gmailconnector
-
-from modules.settings import LOGGER, config
-
-
-def custom_time(*args: logging.Formatter or time.time) -> time.struct_time:
- """Creates custom timezone for ``logging`` which gets used only when invoked by ``Docker``.
-
- This is used only when triggered within a ``docker container`` as it uses UTC timezone.
-
- Args:
- *args: Takes ``Formatter`` object and current epoch time as arguments passed by ``formatTime`` from ``logging``.
-
- Returns:
- struct_time:
- A struct_time object which is a tuple of:
- **current year, month, day, hour, minute, second, weekday, year day and dst** *(Daylight Saving Time)*
- """
- LOGGER.debug(args)
- local_timezone = datetime.now(tz=timezone.utc).astimezone().tzinfo
- return datetime.now().astimezone(tz=local_timezone).timetuple()
-
-
-if config.docker:
- logging.Formatter.converter = custom_time
-
-
-def notify(msg: str) -> NoReturn:
- """Send an email notification when there is a threat.
-
- Args:
- msg: Message that has to be sent.
- """
- if config.gmail_user and config.gmail_pass and config.recipient:
- emailer = gmailconnector.SendEmail(gmail_user=config.gmail_user,
- gmail_pass=config.gmail_pass)
- response = emailer.send_email(recipient=config.recipient,
- subject=f"Netscan Alert - {datetime.now().strftime('%C')}", body=msg)
- if response.ok:
- LOGGER.info("Firewall alert has been sent to '%s'" % config.phone)
- else:
- LOGGER.error("Failed to send a notification.\n%s" % response.body)
- else:
- LOGGER.info("Env variables not found to trigger notification.")
diff --git a/modules/settings.py b/modules/settings.py
deleted file mode 100644
index 24e1990..0000000
--- a/modules/settings.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import logging
-import os
-
-import dotenv
-
-dotenv.load_dotenv(dotenv_path=".env")
-
-if not os.path.isdir('fileio'):
- os.makedirs('fileio')
-
-LOGGER = logging.getLogger(__name__)
-handler = logging.StreamHandler()
-handler.setFormatter(fmt=logging.Formatter(
- fmt="%(asctime)s - [%(levelname)s] - %(name)s - %(funcName)s - Line: %(lineno)d - %(message)s",
- datefmt='%b-%d-%Y %H:%M:%S'
-))
-LOGGER.setLevel(level=logging.DEBUG)
-LOGGER.addHandler(hdlr=handler)
-
-
-class Config:
- """Wrapper for all the environment variables."""
-
- router_pass = os.environ.get('ROUTER_PASS') or os.environ.get('router_pass')
- gmail_user = os.environ.get('GMAIL_USER') or os.environ.get('gmail_user')
- gmail_pass = os.environ.get('GMAIL_PASS') or os.environ.get('gmail_pass')
- recipient = os.environ.get('RECIPIENT') or os.environ.get('recipient')
- docker = os.environ.get('DOCKER') or os.environ.get('docker')
- phone = os.environ.get('PHONE') or os.environ.get('phone')
- snapshot = os.path.join('fileio', 'snapshot.json')
- blocked = os.path.join('fileio', 'blocked.yaml')
-
-
-config = Config()
diff --git a/netsec/__init__.py b/netsec/__init__.py
new file mode 100644
index 0000000..72697aa
--- /dev/null
+++ b/netsec/__init__.py
@@ -0,0 +1,7 @@
+"""Place holder for package."""
+
+from netsec.analyzer import network_monitor # noqa: F401
+from netsec.modules.models import SupportedModules # noqa: F401
+from netsec.modules.settings import config # noqa: F401
+
+version = "0.1.7"
diff --git a/analyzer.py b/netsec/analyzer.py
similarity index 64%
rename from analyzer.py
rename to netsec/analyzer.py
index da3d3dd..4cee253 100644
--- a/analyzer.py
+++ b/netsec/analyzer.py
@@ -1,20 +1,26 @@
from typing import NoReturn
-from modules import att, models, netgear
+from gmailconnector.validator import address as email_address
+from netsec.modules import att, models, netgear, settings
-def network_monitor(module: models.SupportedModules, init: bool = True) -> NoReturn:
+
+def network_monitor(module: models.SupportedModules, init: bool = True, block: bool = False) -> NoReturn:
"""Monitor devices connected to the network.
Args:
module: Module to scan. Currently, supports any network on a Netgear router or At&t networks.
init: Takes a boolean value to create a snapshot file or actually monitor the network.
+ block: Takes a boolean value whether to block the intrusive device.
"""
+ if settings.config.recipient:
+ email_address.logger = settings.LOGGER
+ settings.config.recipient = email_address.ValidateAddress(address=settings.config.recipient) # noqa
if module == models.SupportedModules.netgear:
if init:
netgear.LocalIPScan().create_snapshot()
else:
- netgear.LocalIPScan().run()
+ netgear.LocalIPScan().run(block=block)
elif module == models.SupportedModules.att:
if init:
att.create_snapshot()
@@ -25,7 +31,3 @@ def network_monitor(module: models.SupportedModules, init: bool = True) -> NoRet
"\n\nnetwork argument should either be '%s' or '%s'" % (models.SupportedModules.att,
models.SupportedModules.netgear)
)
-
-
-if __name__ == '__main__':
- network_monitor(module=models.SupportedModules.att, init=False)
diff --git a/modules/att.py b/netsec/modules/att.py
similarity index 86%
rename from modules/att.py
rename to netsec/modules/att.py
index a0056fb..363cd06 100644
--- a/modules/att.py
+++ b/netsec/modules/att.py
@@ -8,8 +8,8 @@
import requests
from pandas import DataFrame
-from modules.helper import notify
-from modules.settings import LOGGER, config
+from netsec.modules.helper import notify
+from netsec.modules.settings import LOGGER, config
SOURCE = "http://{NETWORK_ID}.254/cgi-bin/devices.ha"
@@ -73,7 +73,10 @@ def generate_dataframe() -> DataFrame:
else:
if response.ok:
html_source = response.text
- html_tables = pandas.read_html(html_source)
+ try:
+ html_tables = pandas.read_html(html_source)
+ except ImportError:
+ raise ValueError("No tables found")
return html_tables[0]
else:
LOGGER.error("[%s] - %s" % (response.status_code, response.text))
@@ -122,23 +125,22 @@ def create_snapshot() -> NoReturn:
def run() -> NoReturn:
"""Trigger to initiate a Network Scan and block the devices that are not present in ``snapshot.json`` file."""
if not os.path.isfile(config.snapshot):
- LOGGER.error("'%s' not found. Please run `create_snapshot()` and review it." % config.snapshot)
+ LOGGER.error("'%s' not found. Please pass `init=True` to generate snapshot and review it." % config.snapshot)
raise FileNotFoundError(
"'%s' is required" % config.snapshot
)
with open(config.snapshot) as file:
device_list = json.load(file)
stored_ips = list(device_list.keys())
- threat = ''
+ threats = []
for device in get_attached_devices():
if device.ipv4_address and device.ipv4_address not in stored_ips:
- # SOURCE = "http://{NETWORK_ID}.254/cgi-bin/devices.ha"
+ # REMOTE = "http://{NETWORK_ID}.254/cgi-bin/remoteaccess.ha"
LOGGER.warning('{name} [{ip}: {mac}] is connected to your network.'.format(name=device.name,
mac=device.mac_address,
ip=device.ipv4_address))
- threat += '\nName: {name}\nIP: {ip}\nMAC: {mac}'.format(name=device.name, mac=device.mac_address,
- ip=device.ipv4_address)
- if threat:
- notify(msg=threat)
+ threats.append(dict(Name=device.name, MAC=device.mac_address.upper(), IP=device.ipv4_address))
+ if threats:
+ notify(msg_dict=threats)
else:
- LOGGER.info('NetScan has completed. No threats found on your network.')
+ LOGGER.info('NetSec has completed. No threats found on your network.')
diff --git a/netsec/modules/email_template.html b/netsec/modules/email_template.html
new file mode 100644
index 0000000..6a778f2
--- /dev/null
+++ b/netsec/modules/email_template.html
@@ -0,0 +1,24 @@
+NetSec Intrusion Alert
+
+
+
+
+ {% for alert in alerts %}
+
+ Category
+ Value
+
+ {% for key, value in alert.items() %}
+
+ {{ key }}
+ {{ value }}
+
+ {% endfor %}
+
+ {% endfor %}
+
+
+
+
Source code: https://github.com/thevickypedia/NetSec
+
Reach out: https://vigneshrao.com/contact
+
diff --git a/netsec/modules/helper.py b/netsec/modules/helper.py
new file mode 100644
index 0000000..e513c49
--- /dev/null
+++ b/netsec/modules/helper.py
@@ -0,0 +1,57 @@
+import time
+import os
+from datetime import datetime
+from typing import Dict, List, NoReturn
+
+import gmailconnector
+import jinja2
+from gmailconnector.responder import Response
+
+from netsec.modules.settings import LOGGER, config
+
+
+def _log_response(response: Response) -> NoReturn:
+ """Log response from gmailconnector."""
+ if response.ok:
+ LOGGER.info(response.body)
+ return True
+ LOGGER.error("Failed to send a notification.\n%s" % response.body)
+
+
+def notify(msg_dict: List[Dict[str, str]]) -> NoReturn:
+ """Send an email notification when there is a threat.
+
+ Args:
+ msg_dict: Dict message to be sent as template.
+ """
+ if not config.gmail_user and not config.gmail_pass and not (config.recipient or config.phone):
+ LOGGER.info("Env variables not found to trigger notifications.")
+ return
+ if os.path.isfile(config.notification):
+ with open(config.notification) as file:
+ updated = file.read()
+ if updated and time.time() - float(updated) < 3_600:
+ LOGGER.info("Last notification was sent within an hour.")
+ return
+ sub = f"NetSec Alert - {datetime.now().strftime('%c')}"
+ if config.recipient:
+ with open(os.path.join(os.path.dirname(__file__), 'email_template.html')) as file:
+ template = jinja2.Template(file.read())
+ rendered = template.render(alerts=msg_dict)
+ emailer = gmailconnector.SendEmail(gmail_user=config.gmail_user, gmail_pass=config.gmail_pass)
+ response = emailer.send_email(recipient=config.recipient.email, sender="NetSec",
+ subject=sub, html_body=rendered)
+ if _log_response(response=response):
+ with open(config.notification, 'w') as file:
+ file.write(time.time().__str__())
+ if config.phone:
+ msg = ""
+ for part in msg_dict:
+ for key, value in part.items():
+ msg += "%s: %s\n" % (key, value)
+ msg += "\n"
+ messenger = gmailconnector.SendSMS(gmail_user=config.gmail_user, gmail_pass=config.gmail_pass)
+ response = messenger.send_sms(message=msg, subject=sub)
+ if _log_response(response=response):
+ with open(config.notification, 'w') as file:
+ file.write(time.time().__str__())
diff --git a/modules/models.py b/netsec/modules/models.py
similarity index 100%
rename from modules/models.py
rename to netsec/modules/models.py
diff --git a/modules/netgear.py b/netsec/modules/netgear.py
similarity index 93%
rename from modules/netgear.py
rename to netsec/modules/netgear.py
index 7cabbd2..3183b0d 100644
--- a/modules/netgear.py
+++ b/netsec/modules/netgear.py
@@ -7,9 +7,9 @@
import yaml
from pynetgear import Device, Netgear
-from modules.helper import notify
-from modules.models import DeviceStatus
-from modules.settings import LOGGER, config
+from netsec.modules.helper import notify
+from netsec.modules.models import DeviceStatus
+from netsec.modules.settings import LOGGER, config
class LocalIPScan:
@@ -179,14 +179,15 @@ def always_allow(self, device: Device or str) -> NoReturn:
def run(self, block: bool = False) -> NoReturn:
"""Trigger to initiate a Network Scan and block the devices that are not present in ``snapshot.json`` file."""
if not os.path.isfile(config.snapshot):
- LOGGER.error("'%s' not found. Please generate one and review it." % config.snapshot)
+ LOGGER.error("'%s' not found. Please pass `init=True` to generate "
+ "snapshot and review it." % config.snapshot)
raise FileNotFoundError(
'%s is required' % config.snapshot
)
with open(config.snapshot) as file:
device_list = json.load(file)
stored_ips = list(device_list.keys())
- threat = ''
+ threat = []
blocked = list(self._get_blocked())
for device in self._get_devices():
if device.ip and device.ip not in stored_ips:
@@ -200,12 +201,11 @@ def run(self, block: bool = False) -> NoReturn:
self._dump_blocked(device=device)
else:
LOGGER.info("'%s' is a part of deny list." % device.name)
- threat += '\nName: {name}\nIP: {ip}\nMAC: {mac}'.format(name=device.name, ip=device.ip,
- mac=device.mac)
+ threat.append(dict(Name=device.name, IP=device.ip, MAC=device.mac))
else:
LOGGER.info("'%s' does not have internet access." % device.name)
if threat:
- notify(msg=threat)
+ notify(msg_dict=threat)
else:
- LOGGER.info('NetScan has completed. No threats found on your network.')
+ LOGGER.info('NetSec has completed. No threats found on your network.')
diff --git a/netsec/modules/settings.py b/netsec/modules/settings.py
new file mode 100644
index 0000000..464180d
--- /dev/null
+++ b/netsec/modules/settings.py
@@ -0,0 +1,35 @@
+import logging
+import os
+from typing import AnyStr
+
+import dotenv
+
+dotenv.load_dotenv(dotenv_path=".env")
+
+if not os.path.isdir('fileio'):
+ os.makedirs('fileio')
+
+LOGGER = logging.getLogger(__name__)
+handler = logging.StreamHandler()
+handler.setFormatter(fmt=logging.Formatter(
+ fmt="%(asctime)s - [%(levelname)s] - %(name)s - %(funcName)s - Line: %(lineno)d - %(message)s",
+ datefmt='%b-%d-%Y %H:%M:%S'
+))
+LOGGER.setLevel(level=logging.DEBUG)
+LOGGER.addHandler(hdlr=handler)
+
+
+class Config:
+ """Wrapper for all the environment variables."""
+
+ router_pass: AnyStr = os.environ.get('ROUTER_PASS') or os.environ.get('router_pass')
+ gmail_user: AnyStr = os.environ.get('GMAIL_USER') or os.environ.get('gmail_user')
+ gmail_pass: AnyStr = os.environ.get('GMAIL_PASS') or os.environ.get('gmail_pass')
+ recipient: AnyStr = os.environ.get('RECIPIENT') or os.environ.get('recipient')
+ phone: AnyStr = os.environ.get('PHONE') or os.environ.get('phone')
+ snapshot: os.PathLike = os.path.join('fileio', 'snapshot.json')
+ blocked: os.PathLike = os.path.join('fileio', 'blocked.yaml')
+ notification: os.PathLike = os.path.join('fileio', 'last_notify')
+
+
+config = Config()
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..c5655ad
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,50 @@
+[project]
+name = "NetSec"
+dynamic = ["version"]
+description = "Python module to analyze devices connected to the router and alert accordingly."
+readme = "README.md"
+authors = [{ name = "Vignesh Sivanandha Rao", email = "svignesh1793@gmail.com" }]
+license = { file = "LICENSE" }
+classifiers = [
+ "License :: OSI Approved :: MIT License",
+ "Programming Language :: Python :: 3",
+ "Development Status :: 5 - Production/Stable",
+ "Operating System :: MacOS :: MacOS X",
+ "Operating System :: Microsoft :: Windows",
+ "Operating System :: POSIX :: Linux",
+ "Topic :: System :: Networking :: Firewalls",
+ "Topic :: System :: Networking :: Monitoring :: Hardware Watchdog"
+]
+keywords = ["NetSec", "network-security", "lan", "wlan"]
+requires-python = ">=3"
+dependencies = [
+ "pynetgear==0.10.9",
+ "python-dotenv",
+ "pytz",
+ "PyYAML",
+ "requests",
+ "pandas",
+ "lxml",
+ "gmail-connector",
+ "Jinja2"
+]
+
+[tool.setuptools]
+packages = ["netsec"]
+
+[tool.setuptools.dynamic]
+version = {attr = "netsec.version"}
+
+[build-system]
+requires = ["setuptools", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[project.optional-dependencies]
+dev = ["pytest", "pre-commit"]
+
+[project.urls]
+Homepage = "https://github.com/thevickypedia/NetSec"
+Docs = "https://thevickypedia.github.io/NetSec/"
+Source = "https://github.com/thevickypedia/NetSec"
+"Bug Tracker" = "https://github.com/thevickypedia/NetSec/issues"
+"Release Notes" = "https://github.com/thevickypedia/NetSec/blob/main/release_notes.rst"
diff --git a/release_notes.rst b/release_notes.rst
index 93845e7..32bce80 100644
--- a/release_notes.rst
+++ b/release_notes.rst
@@ -1,6 +1,20 @@
Release Notes
=============
+0.1.7 (02/25/2023)
+------------------
+- Make `NetSec` pip installable
+- Do not repeat notifications in under an hour
+- Onboard github actions to deploy to pypi
+- Update .gitignore, README.md and release_notes.rst
+
+0.1.6 (02/24/2023)
+------------------
+- Support `AT&T` networks
+- Restructure code to improve speed and usability
+- Update README.md, docs and requirements.txt
+- Remove Dockerfile
+
0.1.5 (04/16/2022)
------------------
- Update docstrings
diff --git a/requirements.txt b/requirements.txt
index 4fc25dc..a0f0e4e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,8 +1,4 @@
-pynetgear==0.10.9
-python-dotenv
-pytz
-PyYAML
-requests
-pandas
-lxml
-gmail-connector
\ No newline at end of file
+-e .
+sphinx==5.1.1
+pre-commit
+recommonmark
\ No newline at end of file